/* * Copyright (c) 2018 ARM Limited. All rights reserved. * SPDX-License-Identifier: Apache-2.0 * Licensed under the Apache License, Version 2.0 (the License); you may * not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an AS IS BASIS, WITHOUT * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #ifndef MBED_TDBSTORE_H #define MBED_TDBSTORE_H #include #include #include "KVStore.h" #include "BlockDevice.h" #include "BufferedBlockDevice.h" #include "PlatformMutex.h" namespace mbed { /** TDBStore class * * Lightweight Key Value storage over a block device */ class TDBStore : public KVStore { public: static const uint32_t RESERVED_AREA_SIZE = 64; /** * @brief Class constructor * * @param[in] bd Underlying block device. The BlockDevice * can be any BlockDevice with flash characteristics. * If using a BlockDevice without flash, such as SDBlockDevice, * please add the FlashSimBlockDevice on top of it. * * @returns none */ TDBStore(BlockDevice *bd); /** * @brief Class destructor * * @returns none */ virtual ~TDBStore(); /** * @brief Initialize TDBStore. If data exists, TDBStore will check the data integrity * on initialize. If the integrity checks fails, the TDBStore will use GC to collect * the available data and clean corrupted and erroneous records. * * @returns MBED_SUCCESS Success. * @returns Negative error code on failure. */ virtual int init(); /** * @brief Deinitialize TDBStore, release and free resources. * * @returns MBED_SUCCESS Success. */ virtual int deinit(); /** * @brief Reset TDBStore contents (clear all keys) * * @returns MBED_SUCCESS Success. * MBED_ERROR_NOT_READY Not initialized. * MBED_ERROR_READ_FAILED Unable to read from media. * MBED_ERROR_WRITE_FAILED Unable to write to media. */ virtual int reset(); /** * @brief Set one TDBStore item, given key and value. * * @param[in] key Key - must not include '*' '/' '?' ':' ';' '\' '"' '|' ' ' '<' '>' '\'. * @param[in] buffer Value data buffer. * @param[in] size Value data size. * @param[in] create_flags Flag mask. * * @returns MBED_SUCCESS Success. * MBED_ERROR_NOT_READY Not initialized. * MBED_ERROR_READ_FAILED Unable to read from media. * MBED_ERROR_WRITE_FAILED Unable to write to media. * MBED_ERROR_INVALID_ARGUMENT Invalid argument given in function arguments. * MBED_ERROR_INVALID_SIZE Invalid size given in function arguments. * MBED_ERROR_MEDIA_FULL Not enough room on media. * MBED_ERROR_WRITE_PROTECTED Already stored with "write once" flag. */ virtual int set(const char *key, const void *buffer, size_t size, uint32_t create_flags); /** * @brief Get one TDBStore item by given key. * * @param[in] key Key - must not include '*' '/' '?' ':' ';' '\' '"' '|' ' ' '<' '>' '\'. * @param[in] buffer Value data buffer. * @param[in] buffer_size Value data buffer size. * @param[out] actual_size Actual read size. * @param[in] offset Offset to read from in data. * * @returns MBED_SUCCESS Success. * MBED_ERROR_NOT_READY Not initialized. * MBED_ERROR_READ_FAILED Unable to read from media. * MBED_ERROR_INVALID_ARGUMENT Invalid argument given in function arguments. * MBED_ERROR_INVALID_SIZE Invalid size given in function arguments. * MBED_ERROR_INVALID_DATA_DETECTED Data is corrupt. * MBED_ERROR_ITEM_NOT_FOUND No such key. */ virtual int get(const char *key, void *buffer, size_t buffer_size, size_t *actual_size = NULL, size_t offset = 0); /** * @brief Get information of a given key. The returned info contains size and flags * * @param[in] key Key - must not include '*' '/' '?' ':' ';' '\' '"' '|' ' ' '<' '>' '\'. * @param[out] info Returned information structure. * * @returns MBED_SUCCESS Success. * MBED_ERROR_NOT_READY Not initialized. * MBED_ERROR_READ_FAILED Unable to read from media. * MBED_ERROR_INVALID_ARGUMENT Invalid argument given in function arguments. * MBED_ERROR_INVALID_DATA_DETECTED Data is corrupt. * MBED_ERROR_ITEM_NOT_FOUND No such key. */ virtual int get_info(const char *key, info_t *info); /** * @brief Remove a TDBStore item by given key. * * @param[in] key Key - must not include '*' '/' '?' ':' ';' '\' '"' '|' ' ' '<' '>' '\'. * * @returns MBED_SUCCESS Success. * MBED_ERROR_NOT_READY Not initialized. * MBED_ERROR_READ_FAILED Unable to read from media. * MBED_ERROR_WRITE_FAILED Unable to write to media. * MBED_ERROR_INVALID_ARGUMENT Invalid argument given in function arguments. * MBED_ERROR_MEDIA_FULL Not enough room on media. * MBED_ERROR_ITEM_NOT_FOUND No such key. * MBED_ERROR_WRITE_PROTECTED Already stored with "write once" flag. */ virtual int remove(const char *key); /** * @brief Start an incremental TDBStore set sequence. This operation is blocking other operations. * Any get/set/remove/iterator operation will be blocked until set_finalize is called. * * @param[out] handle Returned incremental set handle. * @param[in] key Key - must not include '*' '/' '?' ':' ';' '\' '"' '|' ' ' '<' '>' '\'. * @param[in] final_data_size Final value data size. * @param[in] create_flags Flag mask. * * @returns MBED_SUCCESS Success. * MBED_ERROR_NOT_READY Not initialized. * MBED_ERROR_READ_FAILED Unable to read from media. * MBED_ERROR_WRITE_FAILED Unable to write to media. * MBED_ERROR_INVALID_ARGUMENT Invalid argument given in function arguments. * MBED_ERROR_INVALID_SIZE Invalid size given in function arguments. * MBED_ERROR_MEDIA_FULL Not enough room on media. * MBED_ERROR_WRITE_PROTECTED Already stored with "write once" flag. */ virtual int set_start(set_handle_t *handle, const char *key, size_t final_data_size, uint32_t create_flags); /** * @brief Add data to incremental TDBStore set sequence. This operation is blocking other operations. * Any get/set/remove operation will be blocked until set_finalize will be called. * * @param[in] handle Incremental set handle. * @param[in] value_data Value data to add. * @param[in] data_size Value data size. * * @returns MBED_SUCCESS Success. * MBED_ERROR_NOT_READY Not initialized. * MBED_ERROR_WRITE_FAILED Unable to write to media. * MBED_ERROR_INVALID_ARGUMENT Invalid argument given in function arguments. * MBED_ERROR_INVALID_SIZE Invalid size given in function arguments. */ virtual int set_add_data(set_handle_t handle, const void *value_data, size_t data_size); /** * @brief Finalize an incremental KVStore set sequence. * * @param[in] handle Incremental set handle. * * @returns MBED_SUCCESS Success. * MBED_ERROR_NOT_READY Not initialized. * MBED_ERROR_WRITE_FAILED Unable to write to media. * MBED_ERROR_INVALID_ARGUMENT Invalid argument given in function arguments. */ virtual int set_finalize(set_handle_t handle); /** * @brief Start an iteration over KVStore keys. * There are no issues with any other operations while iterator is open. * * @param[out] it Returned iterator handle. * @param[in] prefix Key prefix (null for all keys). * * @returns MBED_SUCCESS Success. * MBED_ERROR_NOT_READY Not initialized. * MBED_ERROR_INVALID_ARGUMENT Invalid argument given in function arguments. */ virtual int iterator_open(iterator_t *it, const char *prefix = NULL); /** * @brief Get next key in iteration. * There are no issues with any other operations while iterator is open. * * @param[in] it Iterator handle. * @param[in] key Buffer for returned key. * @param[in] key_size Key buffer size. * * @returns MBED_SUCCESS Success. * MBED_ERROR_NOT_READY Not initialized. * MBED_ERROR_READ_FAILED Unable to read from block device. * MBED_ERROR_INVALID_ARGUMENT Invalid argument given in function arguments. * MBED_ERROR_INVALID_SIZE Invalid size given in function arguments. * MBED_ERROR_INVALID_DATA_DETECTED Data is corrupt. * MBED_ERROR_ITEM_NOT_FOUND No more keys found. */ virtual int iterator_next(iterator_t it, char *key, size_t key_size); /** * @brief Close iteration. * * @param[in] it Iterator handle. * * @returns MBED_SUCCESS Success. * MBED_ERROR_NOT_READY Not initialized. * MBED_ERROR_INVALID_ARGUMENT Invalid argument given in function arguments. */ virtual int iterator_close(iterator_t it); /** * @brief Set data in reserved area, which is a special location for special data, such as ROT. * The data written to reserved area can't be overwritten. * * @param[in] reserved_data Reserved data buffer. * @param[in] reserved_data_buf_size * Reserved data buffer size. * * @returns MBED_SUCCESS Success. * MBED_ERROR_NOT_READY Not initialized. * MBED_ERROR_READ_FAILED Unable to read from media. * MBED_ERROR_WRITE_FAILED Unable to write to media. * MBED_ERROR_INVALID_ARGUMENT Invalid argument given in function arguments. * MBED_ERROR_INVALID_SIZE Invalid size given in function arguments. */ virtual int reserved_data_set(const void *reserved_data, size_t reserved_data_buf_size); /** * @brief Get data from reserved area, which is a special location for special data, such as ROT. * * @param[in] reserved_data Reserved data buffer. * @param[in] reserved_data_buf_size * Reserved data buffer size. * @param[in] actual_data_size Return data size. * * @returns MBED_SUCCESS Success. * MBED_ERROR_NOT_READY Not initialized. * MBED_ERROR_READ_FAILED Unable to read from media. * MBED_ERROR_INVALID_ARGUMENT Invalid argument given in function arguments. * MBED_ERROR_INVALID_DATA_DETECTED Data is corrupt. * MBED_ERROR_ITEM_NOT_FOUND No reserved data was written. */ virtual int reserved_data_get(void *reserved_data, size_t reserved_data_buf_size, size_t *actual_data_size = 0); #if !defined(DOXYGEN_ONLY) private: typedef struct { uint32_t address; size_t size; } tdbstore_area_data_t; static const int _num_areas = 2; static const int _max_open_iterators = 16; PlatformMutex _mutex; PlatformMutex _inc_set_mutex; void *_ram_table; size_t _max_keys; size_t _num_keys; BlockDevice *_bd; BufferedBlockDevice *_buff_bd; uint32_t _free_space_offset; uint32_t _master_record_offset; uint32_t _master_record_size; bool _is_initialized; int _active_area; uint16_t _active_area_version; size_t _size; tdbstore_area_data_t _area_params[_num_areas]; uint32_t _prog_size; uint8_t *_work_buf; char *_key_buf; bool _variant_bd_erase_unit_size; void *_inc_set_handle; void *_iterator_table[_max_open_iterators]; /** * @brief Read a block from an area. * * @param[in] area Area. * @param[in] offset Offset in area. * @param[in] size Number of bytes to read. * @param[in] buf Output buffer. * * @returns 0 for success, nonzero for failure. */ int read_area(uint8_t area, uint32_t offset, uint32_t size, void *buf); /** * @brief Write a block to an area. * * @param[in] area Area. * @param[in] offset Offset in area. * @param[in] size Number of bytes to write. * @param[in] buf Input buffer. * * @returns 0 for success, non-zero for failure. */ int write_area(uint8_t area, uint32_t offset, uint32_t size, const void *buf); /** * @brief Reset an area (erase its start). * * @param[in] area Area. * * @returns 0 for success, nonzero for failure. */ int reset_area(uint8_t area); /** * @brief Erase an erase unit. * * @param[in] area Area. * @param[in] offset Offset in area. * * @returns 0 for success, nonzero for failure. */ int erase_erase_unit(uint8_t area, uint32_t offset); /** * @brief Calculate addresses and sizes of areas. */ void calc_area_params(); /** * @brief Read a TDBStore record from a given location. * * @param[in] area Area. * @param[in] offset Offset of record in area. * @param[in] key Key - must not include '*' '/' '?' ':' ';' '\' '"' '|' ' ' '<' '>' '\'. * @param[in] data_buf Data buffer. * @param[in] data_buf_size Data buffer size. * @param[out] actual_data_size Actual data size. * @param[in] data_offset Offset in data. * @param[in] copy_key Copy key to user buffer. * @param[in] copy_data Copy data to user buffer. * @param[in] check_expected_key Check whether key belongs to this record. * @param[in] calc_hash Calculate hash (on key). * @param[out] hash Calculated hash. * @param[out] flags Record flags. * @param[out] next_offset Offset of next record. * * @returns 0 for success, nonzero for failure. */ int read_record(uint8_t area, uint32_t offset, char *key, void *data_buf, uint32_t data_buf_size, uint32_t &actual_data_size, size_t data_offset, bool copy_key, bool copy_data, bool check_expected_key, bool calc_hash, uint32_t &hash, uint32_t &flags, uint32_t &next_offset); /** * @brief Write a master record of a given area. * * @param[in] area Area. * @param[in] version Area version. * @param[out] next_offset Offset of next record. * * @returns 0 for success, nonzero for failure. */ int write_master_record(uint8_t area, uint16_t version, uint32_t &next_offset); /** * @brief Copy a record from one area to the opposite one. * * @param[in] from_area Area to copy record from. * @param[in] from_offset Offset in source area. * @param[in] to_offset Offset in destination area. * @param[out] to_next_offset Offset of next record in destination area. * * @returns 0 for success, nonzero for failure. */ int copy_record(uint8_t from_area, uint32_t from_offset, uint32_t to_offset, uint32_t &to_next_offset); /** * @brief Garbage collection (compact all records from active area to the standby one). * * @returns 0 for success, nonzero for failure. */ int garbage_collection(); /** * @brief Return record size given key and data size. * * @param[in] key Key - must not include '*' '/' '?' ':' ';' '\' '"' '|' ' ' '<' '>' '\'. * @param[in] data_size Data size. * * @returns record size. */ uint32_t record_size(const char *key, uint32_t data_size); /** * @brief Find a record given key * * @param[in] area Area. * @param[in] key Key - must not include '*' '/' '?' ':' ';' '\' '"' '|' ' ' '<' '>' '\'. * @param[out] offset Offset of record. * @param[out] ram_table_ind Index in RAM table (target one if not found). * @param[out] hash Calculated key hash. * * @returns 0 for success, nonzero for failure. */ int find_record(uint8_t area, const char *key, uint32_t &offset, uint32_t &ram_table_ind, uint32_t &hash); /** * @brief Actual logics of get API (also covers all other get APIs). * * @param[in] key Key - must not include '*' '/' '?' ':' ';' '\' '"' '|' ' ' '<' '>' '\'. * @param[in] copy_data Copy data to user buffer. * @param[in] data_buf Buffer to store data on. * @param[in] data_buf_size Data buffer size (bytes). * @param[out] actual_data_size Actual data size (bytes). * @param[out] flags Flags. * * @returns 0 for success, nonzero for failure. */ int do_get(const char *key, bool copy_data, void *data_buf, uint32_t data_buf_size, uint32_t &actual_data_size, uint32_t &flags); /** * @brief Actual logics of set API (covers also the remove API). * * @param[in] key Key - must not include '*' '/' '?' ':' ';' '\' '"' '|' ' ' '<' '>' '\'. * @param[in] data_buf Data buffer. * @param[in] data_buf_size Data buffer size (bytes). * @param[in] flags Flags. * * @returns 0 for success, nonzero for failure. */ int do_set(const char *key, const void *data_buf, uint32_t data_buf_size, uint32_t flags); /** * @brief Build RAM table and update _free_space_offset (scanning all the records in the area). * * @returns 0 for success, nonzero for failure. */ int build_ram_table(); /** * @brief Increment maximum number of keys and reallocate RAM table accordingly. * * @param[out] ram_table Updated RAM table. * * @returns 0 for success, nonzero for failure. */ int increment_max_keys(void **ram_table = 0); /** * @brief Calculate offset from start of erase unit. * * @param[in] area Area. * @param[in] offset Offset in area. * @param[out] offset_from_start Offset from start of erase unit. * @param[out] dist_to_end Distance to end of erase unit. * * @returns offset in erase unit. */ void offset_in_erase_unit(uint8_t area, uint32_t offset, uint32_t &offset_from_start, uint32_t &dist_to_end); /** * @brief Check whether erase unit is erased (from offset until end of unit). * * @param[in] area Area. * @param[in] offset Offset in area. * @param[out] erased Unit is erased. * * @returns 0 for success, nonzero for failure. */ int is_erase_unit_erased(uint8_t area, uint32_t offset, bool &erased); /** * @brief Before writing a record, check whether you are crossing an erase unit. * If you do, check if it's erased, and erase it if not. * * @param[in] area Area. * @param[in] offset Offset in area. * @param[in] size Write size. * @param[in] force_check Force checking. * * @returns 0 for success, nonzero for failure. */ int check_erase_before_write(uint8_t area, uint32_t offset, uint32_t size, bool force_check = false); /** * @brief Get data from reserved area - worker function. * * @param[in] reserved_data Reserved data buffer (0 to return nothing). * @param[in] reserved_data_buf_size * Reserved data buffer size. * @param[in] actual_data_size Return data size. * * @returns 0 on success or a negative error code on failure */ int do_reserved_data_get(void *reserved_data, size_t reserved_data_buf_size, size_t *actual_data_size = 0); /** * @brief Update all iterators after adding or deleting of keys. * * @param[in] added True if added, false if deleted. * @param[in] ram_table_ind RAM table index. * * @returns none */ void update_all_iterators(bool added, uint32_t ram_table_ind); #endif }; /** @}*/ } // namespace mbed #endif