mirror of
https://github.com/espressif/esp-idf.git
synced 2026-10-01 18:50:34 +03:00
Merge branch 'feature/nvs_flash_purge_erased_v6.0' into 'release/v6.0'
nvs_flash: Added purging of erased items at namespace (handle) level. (v6.0) See merge request espressif/esp-idf!46909
This commit is contained in:
@@ -15,6 +15,7 @@
|
||||
#include <unistd.h> // for close(), read(), write(), lseek()
|
||||
#include "nvs_constants.h" // for NVS_CONST_PAGE_SIZE
|
||||
#include "esp_log.h" // for ESP_LOGE
|
||||
#include "nvs.h" // for ESP_ERR_NVS_NOT_FOUND
|
||||
|
||||
#define TAG "NVSPartitionTestHelper"
|
||||
|
||||
@@ -227,6 +228,48 @@ esp_err_t NVSPartitionTestHelper::load_from_file(const char *part_name, const ch
|
||||
return ret;
|
||||
}
|
||||
|
||||
esp_err_t NVSPartitionTestHelper::check_marker(const char *part_name, const uint8_t *marker, size_t marker_len)
|
||||
{
|
||||
if (marker == nullptr || marker_len == 0) {
|
||||
return ESP_ERR_INVALID_ARG;
|
||||
}
|
||||
|
||||
// Find the partition by name
|
||||
const esp_partition_t *part = esp_partition_find_first(
|
||||
ESP_PARTITION_TYPE_DATA,
|
||||
ESP_PARTITION_SUBTYPE_DATA_NVS,
|
||||
part_name);
|
||||
|
||||
if (part == nullptr) {
|
||||
return ESP_ERR_NOT_FOUND;
|
||||
}
|
||||
|
||||
// Allocate buffer for reading partition data
|
||||
uint8_t *read_buf = (uint8_t*)malloc(part->size);
|
||||
if (read_buf == nullptr) {
|
||||
return ESP_ERR_NO_MEM;
|
||||
}
|
||||
|
||||
// Read the entire partition
|
||||
esp_err_t ret = esp_partition_read(part, 0, read_buf, part->size);
|
||||
if (ret != ESP_OK) {
|
||||
free(read_buf);
|
||||
return ret;
|
||||
}
|
||||
|
||||
// Search for the marker in the partition data
|
||||
esp_err_t result = ESP_ERR_NVS_NOT_FOUND;
|
||||
for (size_t i = 0; i <= part->size - marker_len; i++) {
|
||||
if (memcmp(read_buf + i, marker, marker_len) == 0) {
|
||||
result = ESP_OK;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
free(read_buf);
|
||||
return result;
|
||||
}
|
||||
|
||||
void NVSPartitionTestHelper::clear_stats(void)
|
||||
{
|
||||
return esp_partition_clear_stats();
|
||||
@@ -527,6 +570,54 @@ esp_err_t NVSPartitionTestHelper::load_from_file(const char *part_name, const ch
|
||||
return ret;
|
||||
}
|
||||
|
||||
esp_err_t NVSPartitionTestHelper::check_marker(const char *part_name, const uint8_t *marker, size_t marker_len)
|
||||
{
|
||||
if (marker == nullptr || marker_len == 0) {
|
||||
return ESP_ERR_INVALID_ARG;
|
||||
}
|
||||
|
||||
// Get the BDL handle for the partition
|
||||
esp_blockdev_handle_t bdl_handle;
|
||||
|
||||
esp_err_t ret = esp_partition_get_blockdev(
|
||||
ESP_PARTITION_TYPE_DATA,
|
||||
ESP_PARTITION_SUBTYPE_DATA_NVS,
|
||||
part_name,
|
||||
&bdl_handle);
|
||||
|
||||
if (ret != ESP_OK) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
// Allocate buffer for reading partition data
|
||||
size_t read_size = bdl_handle->geometry.disk_size;
|
||||
uint8_t *read_buf = (uint8_t*)malloc(read_size);
|
||||
if (read_buf == nullptr) {
|
||||
return ESP_ERR_NO_MEM;
|
||||
}
|
||||
|
||||
do {
|
||||
// Read the entire partition
|
||||
ret = bdl_handle->ops->read(bdl_handle, read_buf, read_size, 0, read_size);
|
||||
if (ret != ESP_OK) {
|
||||
break;
|
||||
}
|
||||
|
||||
// Search for the marker in the partition data
|
||||
ret = ESP_ERR_NVS_NOT_FOUND;
|
||||
for (size_t i = 0; i <= bdl_handle->geometry.disk_size - marker_len; i++) {
|
||||
if (memcmp(read_buf + i, marker, marker_len) == 0) {
|
||||
ret = ESP_OK;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
} while (false);
|
||||
|
||||
free(read_buf);
|
||||
return ret;
|
||||
}
|
||||
|
||||
void NVSPartitionTestHelper::clear_stats(void)
|
||||
{
|
||||
// TODO: Once BDL implementation supports statistics, we will need to re-implement this function
|
||||
|
||||
@@ -56,6 +56,11 @@ public:
|
||||
// Load the partition from a file
|
||||
static esp_err_t load_from_file(const char *part_name, const char *file_name);
|
||||
|
||||
// Check if a specific marker (byte pattern) exists in the partition
|
||||
// Returns ESP_OK if marker is found, ESP_ERR_NOT_FOUND if marker not found,
|
||||
// or other error codes for technical failures
|
||||
static esp_err_t check_marker(const char *part_name, const uint8_t *marker, size_t marker_len);
|
||||
|
||||
// Set of functions to access partition statistics
|
||||
// At the moment these functions are global, it means that they do not distinguish between
|
||||
// different partitions
|
||||
|
||||
@@ -32,6 +32,8 @@ using namespace std;
|
||||
|
||||
#define WD_PREFIX "./components/nvs_flash/host_test/nvs_host_test/" // path from ci cwd to the location of host test
|
||||
|
||||
#define TEST_DEFAULT_PURGE_AFTER_ERASE true // erase with purge after erase
|
||||
|
||||
#if defined(SEGGER_H) && defined(GLOBAL_H)
|
||||
NVS_GUARD_SYSVIEW_MACRO_EXPANSION_PUSH();
|
||||
#undef U8
|
||||
@@ -155,6 +157,7 @@ TEST_CASE("Page when writing and erasing, used/erased counts are updated correct
|
||||
// The remaining, not deleted entry is the namespace entry.
|
||||
|
||||
NVSPartitionTestHelper h(TEST_DEFAULT_PARTITION_NAME);
|
||||
const bool purgeAfterErase = TEST_DEFAULT_PURGE_AFTER_ERASE;
|
||||
|
||||
nvs::Page page;
|
||||
TEST_ESP_OK(page.load(&h, 0));
|
||||
@@ -165,7 +168,7 @@ TEST_CASE("Page when writing and erasing, used/erased counts are updated correct
|
||||
CHECK(page.getUsedEntryCount() == 1);
|
||||
TEST_ESP_OK(page.writeItem(2, "foo1", foo1));
|
||||
CHECK(page.getUsedEntryCount() == 2);
|
||||
TEST_ESP_OK(page.eraseItem<uint32_t>(2, "foo1"));
|
||||
TEST_ESP_OK(page.eraseItem<uint32_t>(2, "foo1", purgeAfterErase));
|
||||
CHECK(page.getUsedEntryCount() == 1);
|
||||
CHECK(page.getErasedEntryCount() == 1);
|
||||
for (size_t i = 0; i < nvs::Page::ENTRY_COUNT - 2; ++i) {
|
||||
@@ -178,7 +181,7 @@ TEST_CASE("Page when writing and erasing, used/erased counts are updated correct
|
||||
for (size_t i = 0; i < nvs::Page::ENTRY_COUNT - 2; ++i) {
|
||||
char name[16];
|
||||
snprintf(name, sizeof(name), "i%ld", (long int)i);
|
||||
TEST_ESP_OK(page.eraseItem(1, nvs::itemTypeOf<size_t>(), name));
|
||||
TEST_ESP_OK(page.eraseItem(1, nvs::itemTypeOf<size_t>(), name, purgeAfterErase));
|
||||
}
|
||||
CHECK(page.getUsedEntryCount() == 1);
|
||||
CHECK(page.getErasedEntryCount() == nvs::Page::ENTRY_COUNT - 1);
|
||||
@@ -447,13 +450,14 @@ TEST_CASE("storage doesn't add duplicates within one page", "[nvs]")
|
||||
// - overwriting the same item sets the erased entry count to 1
|
||||
|
||||
NVSPartitionTestHelper h(TEST_DEFAULT_PARTITION_NAME);
|
||||
const bool purgeAfterErase = TEST_DEFAULT_PURGE_AFTER_ERASE;
|
||||
|
||||
nvs::Storage storage(&h);
|
||||
|
||||
TEST_ESP_OK(storage.init(0, h.get_sectors()));
|
||||
int bar = 0;
|
||||
TEST_ESP_OK(storage.writeItem(1, "bar", ++bar));
|
||||
TEST_ESP_OK(storage.writeItem(1, "bar", ++bar));
|
||||
TEST_ESP_OK(storage.writeItem(1, "bar", ++bar, purgeAfterErase));
|
||||
TEST_ESP_OK(storage.writeItem(1, "bar", ++bar, purgeAfterErase));
|
||||
|
||||
nvs::Page page;
|
||||
|
||||
@@ -468,6 +472,7 @@ TEST_CASE("can write one item a thousand times", "[nvs]")
|
||||
// TC verifies that Storage.writeItem can write one item a thousand times.
|
||||
|
||||
NVSPartitionTestHelper h(TEST_DEFAULT_PARTITION_NAME);
|
||||
const bool purgeAfterErase = TEST_DEFAULT_PURGE_AFTER_ERASE;
|
||||
|
||||
const size_t NO_WRITES = 1000;
|
||||
nvs::Storage storage(&h);
|
||||
@@ -477,7 +482,7 @@ TEST_CASE("can write one item a thousand times", "[nvs]")
|
||||
NVSPartitionTestHelper::clear_stats(); // Clear statistics before writing
|
||||
|
||||
for (size_t i = 0; i < NO_WRITES; ++i) {
|
||||
TEST_ESP_OK(storage.writeItem(1, "i", static_cast<int>(i)));
|
||||
TEST_ESP_OK(storage.writeItem(1, "i", static_cast<int>(i), purgeAfterErase));
|
||||
}
|
||||
s_perf << "Time to write one item a " << NO_WRITES << " times: " << NVSPartitionTestHelper::get_total_time() << " us (" << NVSPartitionTestHelper::get_erase_ops() << " " << NVSPartitionTestHelper::get_write_ops() << " " << NVSPartitionTestHelper::get_read_ops() << " " << NVSPartitionTestHelper::get_write_bytes() << " " << NVSPartitionTestHelper::get_read_bytes() << ")" << std::endl;
|
||||
}
|
||||
@@ -493,17 +498,17 @@ TEST_CASE("storage doesn't add duplicates within multiple pages", "[nvs]")
|
||||
|
||||
// - reading the item "bar" from the second page works
|
||||
NVSPartitionTestHelper h(TEST_DEFAULT_PARTITION_NAME);
|
||||
const bool purgeAfterErase = TEST_DEFAULT_PURGE_AFTER_ERASE;
|
||||
|
||||
nvs::Storage storage(&h);
|
||||
|
||||
TEST_ESP_OK(storage.init(0, h.get_sectors()));
|
||||
int bar = 0;
|
||||
TEST_ESP_OK(storage.writeItem(1, "bar", ++bar));
|
||||
TEST_ESP_OK(storage.writeItem(1, "bar", ++bar, purgeAfterErase));
|
||||
for (size_t i = 0; i < nvs::Page::ENTRY_COUNT; ++i) {
|
||||
TEST_ESP_OK(storage.writeItem(1, "foo", static_cast<int>(++bar)));
|
||||
TEST_ESP_OK(storage.writeItem(1, "foo", static_cast<int>(++bar), purgeAfterErase));
|
||||
}
|
||||
TEST_ESP_OK(storage.writeItem(1, "bar", ++bar));
|
||||
|
||||
TEST_ESP_OK(storage.writeItem(1, "bar", ++bar, purgeAfterErase));
|
||||
nvs::Page page;
|
||||
TEST_ESP_OK(page.load(&h, 0)); // first page attempt
|
||||
CHECK(page.findItem(1, nvs::itemTypeOf<int>(), "bar") == ESP_ERR_NVS_NOT_FOUND);
|
||||
@@ -517,18 +522,19 @@ TEST_CASE("storage can find items on second page if first is not fully written a
|
||||
// The first page is occupied by the item "1" and "2" which occupy enough of the first page in order to not fit the third item "3".
|
||||
|
||||
NVSPartitionTestHelper h(TEST_DEFAULT_PARTITION_NAME);
|
||||
const bool purgeAfterErase = TEST_DEFAULT_PURGE_AFTER_ERASE;
|
||||
|
||||
nvs::Storage storage(&h);
|
||||
TEST_ESP_OK(storage.init(0, h.get_sectors()));
|
||||
|
||||
uint8_t bigdata[(nvs::Page::CHUNK_MAX_SIZE - nvs::Page::ENTRY_SIZE) / 2] = {0};
|
||||
// write one big chunk of data
|
||||
ESP_ERROR_CHECK(storage.writeItem(1, nvs::ItemType::BLOB, "1", bigdata, sizeof(bigdata)));
|
||||
ESP_ERROR_CHECK(storage.writeItem(1, nvs::ItemType::BLOB, "1", bigdata, sizeof(bigdata), purgeAfterErase));
|
||||
// write another big chunk of data
|
||||
ESP_ERROR_CHECK(storage.writeItem(1, nvs::ItemType::BLOB, "2", bigdata, sizeof(bigdata)));
|
||||
ESP_ERROR_CHECK(storage.writeItem(1, nvs::ItemType::BLOB, "2", bigdata, sizeof(bigdata), purgeAfterErase));
|
||||
|
||||
// write third one; it will not fit into the first page
|
||||
ESP_ERROR_CHECK(storage.writeItem(1, nvs::ItemType::BLOB, "3", bigdata, sizeof(bigdata)));
|
||||
ESP_ERROR_CHECK(storage.writeItem(1, nvs::ItemType::BLOB, "3", bigdata, sizeof(bigdata), purgeAfterErase));
|
||||
|
||||
size_t size;
|
||||
ESP_ERROR_CHECK(storage.getItemDataSize(1, nvs::ItemType::BLOB, "1", size));
|
||||
@@ -543,6 +549,7 @@ TEST_CASE("can write and read variable length data lots of times", "[nvs]")
|
||||
// of variable length data interleaved with fixed length data.
|
||||
|
||||
NVSPartitionTestHelper h(TEST_DEFAULT_PARTITION_NAME);
|
||||
const bool purgeAfterErase = TEST_DEFAULT_PURGE_AFTER_ERASE;
|
||||
|
||||
const size_t NO_WRITES = 1000;
|
||||
nvs::Storage storage(&h);
|
||||
@@ -556,8 +563,8 @@ TEST_CASE("can write and read variable length data lots of times", "[nvs]")
|
||||
|
||||
for (size_t i = 0; i < NO_WRITES; ++i) {
|
||||
CAPTURE(i);
|
||||
TEST_ESP_OK(storage.writeItem(1, nvs::ItemType::SZ, "foobaar", str, len + 1));
|
||||
TEST_ESP_OK(storage.writeItem(1, "foo", static_cast<uint32_t>(i)));
|
||||
TEST_ESP_OK(storage.writeItem(1, nvs::ItemType::SZ, "foobaar", str, len + 1, purgeAfterErase));
|
||||
TEST_ESP_OK(storage.writeItem(1, "foo", static_cast<uint32_t>(i), purgeAfterErase));
|
||||
|
||||
uint32_t value;
|
||||
TEST_ESP_OK(storage.readItem(1, "foo", value));
|
||||
@@ -577,6 +584,7 @@ TEST_CASE("can get length of variable length data", "[nvs]")
|
||||
// - getting the size of a string and blob works correctly and independently between namespaces
|
||||
|
||||
NVSPartitionTestHelper h(TEST_DEFAULT_PARTITION_NAME);
|
||||
const bool purgeAfterErase = TEST_DEFAULT_PURGE_AFTER_ERASE;
|
||||
|
||||
TEST_ESP_OK(NVSPartitionTestHelper::randomize_partition(TEST_DEFAULT_PARTITION_NAME, 200));
|
||||
|
||||
@@ -586,12 +594,12 @@ TEST_CASE("can get length of variable length data", "[nvs]")
|
||||
|
||||
const char str[] = "foobar1234foobar1234foobar1234foobar1234foobar1234foobar1234foobar1234foobar1234";
|
||||
size_t len = strlen(str);
|
||||
TEST_ESP_OK(storage.writeItem(1, nvs::ItemType::SZ, "foobaar", str, len + 1)); // namespace 1
|
||||
TEST_ESP_OK(storage.writeItem(1, nvs::ItemType::SZ, "foobaar", str, len + 1, purgeAfterErase)); // namespace 1
|
||||
size_t dataSize;
|
||||
TEST_ESP_OK(storage.getItemDataSize(1, nvs::ItemType::SZ, "foobaar", dataSize)); // namespace 1
|
||||
CHECK(dataSize == len + 1);
|
||||
|
||||
TEST_ESP_OK(storage.writeItem(2, nvs::ItemType::BLOB, "foobaar", str, len)); // namespace 2
|
||||
TEST_ESP_OK(storage.writeItem(2, nvs::ItemType::BLOB, "foobaar", str, len, purgeAfterErase)); // namespace 2
|
||||
TEST_ESP_OK(storage.getItemDataSize(2, nvs::ItemType::BLOB, "foobaar", dataSize)); // namespace 2
|
||||
CHECK(dataSize == len);
|
||||
}
|
||||
@@ -626,6 +634,7 @@ TEST_CASE("storage may become full", "[nvs]")
|
||||
// - writing an item when the storage is full returns ESP_ERR_NVS_NOT_ENOUGH_SPACE
|
||||
|
||||
NVSPartitionTestHelper h(TEST_DEFAULT_PARTITION_NAME);
|
||||
const bool purgeAfterErase = TEST_DEFAULT_PURGE_AFTER_ERASE;
|
||||
|
||||
nvs::Storage storage(&h);
|
||||
|
||||
@@ -638,9 +647,9 @@ TEST_CASE("storage may become full", "[nvs]")
|
||||
for (size_t i = 0; i < max_items; ++i) {
|
||||
char name[nvs::Item::MAX_KEY_LENGTH + 1];
|
||||
snprintf(name, sizeof(name), "key%05d", static_cast<int>(i));
|
||||
TEST_ESP_OK(storage.writeItem(1, name, static_cast<int>(i)));
|
||||
TEST_ESP_OK(storage.writeItem(1, name, static_cast<int>(i), purgeAfterErase));
|
||||
}
|
||||
REQUIRE(storage.writeItem(1, "foo", 10) == ESP_ERR_NVS_NOT_ENOUGH_SPACE);
|
||||
REQUIRE(storage.writeItem(1, "foo", 10, purgeAfterErase) == ESP_ERR_NVS_NOT_ENOUGH_SPACE);
|
||||
}
|
||||
|
||||
TEST_CASE("can reuse the space previously occupied by the overwritten item", "[nvs]")
|
||||
@@ -651,6 +660,7 @@ TEST_CASE("can reuse the space previously occupied by the overwritten item", "[n
|
||||
// marked as erased and thus space occupied by some of them will be reclaimed and page will be erased.
|
||||
|
||||
NVSPartitionTestHelper h(TEST_DEFAULT_PARTITION_NAME);
|
||||
const bool purgeAfterErase = TEST_DEFAULT_PURGE_AFTER_ERASE;
|
||||
|
||||
nvs::Storage storage(&h);
|
||||
TEST_ESP_OK(storage.init(0, h.get_sectors()));
|
||||
@@ -662,7 +672,7 @@ TEST_CASE("can reuse the space previously occupied by the overwritten item", "[n
|
||||
max_usable_items += 1;
|
||||
|
||||
for (size_t i = 0; i < max_usable_items; ++i) {
|
||||
TEST_ESP_OK(storage.writeItem(1, "foo", 42U));
|
||||
TEST_ESP_OK(storage.writeItem(1, "foo", 42U, purgeAfterErase));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -673,6 +683,7 @@ TEST_CASE("erase operations are distributed among sectors", "[nvs]")
|
||||
// on the same item, and finally checks that erase counts are distributed among the remaining sectors.
|
||||
|
||||
NVSPartitionTestHelper h(TEST_DEFAULT_PARTITION_NAME);
|
||||
const bool purgeAfterErase = TEST_DEFAULT_PURGE_AFTER_ERASE;
|
||||
|
||||
// Test parameters
|
||||
const size_t static_pages = 2; // Number of NVS pages to fill with stable items
|
||||
@@ -694,14 +705,14 @@ TEST_CASE("erase operations are distributed among sectors", "[nvs]")
|
||||
for (size_t i = 0; i < static_pages * nvs::Page::ENTRY_COUNT; ++i) {
|
||||
char name[nvs::Item::MAX_KEY_LENGTH];
|
||||
snprintf(name, sizeof(name), "static%d", (int) i);
|
||||
TEST_ESP_OK(storage.writeItem(1, name, i));
|
||||
TEST_ESP_OK(storage.writeItem(1, name, i, purgeAfterErase));
|
||||
}
|
||||
|
||||
// Calculate how many write operations we need to perform to ensure that every page will be erased
|
||||
size_t write_ops = (all_pages - static_pages) * nvs::Page::ENTRY_COUNT * erase_count;
|
||||
|
||||
for (size_t i = 0; i < write_ops; ++i) {
|
||||
TEST_ESP_OK(storage.writeItem(1, "value", i));
|
||||
TEST_ESP_OK(storage.writeItem(1, "value", i, purgeAfterErase));
|
||||
}
|
||||
|
||||
const size_t max_erase_cnt = write_ops / nvs::Page::ENTRY_COUNT / (all_pages - static_pages);
|
||||
@@ -735,6 +746,7 @@ TEST_CASE("can erase items", "[nvs]")
|
||||
// - namespace id is respected in eraseItem and readItem
|
||||
|
||||
NVSPartitionTestHelper h(TEST_DEFAULT_PARTITION_NAME);
|
||||
const bool purgeAfterErase = TEST_DEFAULT_PURGE_AFTER_ERASE;
|
||||
|
||||
nvs::Storage storage(&h);
|
||||
TEST_ESP_OK(storage.init(0, h.get_sectors()));
|
||||
@@ -743,15 +755,15 @@ TEST_CASE("can erase items", "[nvs]")
|
||||
for (size_t i = 0; i < nvs::Page::ENTRY_COUNT * 2 - 3; ++i) {
|
||||
char name[nvs::Item::MAX_KEY_LENGTH + 1];
|
||||
snprintf(name, sizeof(name), "key%05d", static_cast<int>(i));
|
||||
TEST_ESP_OK(storage.writeItem(3, name, static_cast<int>(i)));
|
||||
TEST_ESP_OK(storage.writeItem(3, name, static_cast<int>(i), purgeAfterErase));
|
||||
}
|
||||
TEST_ESP_OK(storage.writeItem(1, "foo", 32));
|
||||
TEST_ESP_OK(storage.writeItem(2, "foo", 64));
|
||||
TEST_ESP_OK(storage.eraseItem(2, "foo"));
|
||||
TEST_ESP_OK(storage.writeItem(1, "foo", 32, purgeAfterErase));
|
||||
TEST_ESP_OK(storage.writeItem(2, "foo", 64, purgeAfterErase));
|
||||
TEST_ESP_OK(storage.eraseItem(2, "foo", purgeAfterErase));
|
||||
int val;
|
||||
TEST_ESP_OK(storage.readItem(1, "foo", val));
|
||||
CHECK(val == 32);
|
||||
TEST_ESP_OK(storage.eraseNamespace(3));
|
||||
TEST_ESP_OK(storage.eraseNamespace(3, purgeAfterErase));
|
||||
CHECK(storage.readItem(2, "foo", val) == ESP_ERR_NVS_NOT_FOUND);
|
||||
CHECK(storage.readItem(3, "key00222", val) == ESP_ERR_NVS_NOT_FOUND);
|
||||
}
|
||||
@@ -2335,6 +2347,7 @@ TEST_CASE("Check that orphaned blobs are erased during init", "[nvs]")
|
||||
|
||||
// Use NVSPartitionTestHelper to provide nvs::Partition instance for the test
|
||||
NVSPartitionTestHelper h(TEST_DEFAULT_PARTITION_NAME);
|
||||
const bool purgeAfterErase = true;
|
||||
|
||||
// Make sure the partition has enough NVS pages
|
||||
// Requires 1 namespace entry, 1 + 3 metadata entries for the blob,
|
||||
@@ -2350,13 +2363,13 @@ TEST_CASE("Check that orphaned blobs are erased during init", "[nvs]")
|
||||
|
||||
TEST_ESP_OK(storage.init(0, required_sectors));
|
||||
|
||||
TEST_ESP_OK(storage.writeItem(1, nvs::ItemType::BLOB, "key", blob, sizeof(blob)));
|
||||
TEST_ESP_OK(storage.writeItem(1, nvs::ItemType::BLOB, "key", blob, sizeof(blob), purgeAfterErase));
|
||||
|
||||
TEST_ESP_OK(storage.init(0, required_sectors));
|
||||
// Check that multi-page item is still available.
|
||||
TEST_ESP_OK(storage.readItem(1, nvs::ItemType::BLOB, "key", blob, sizeof(blob)));
|
||||
|
||||
TEST_ESP_ERR(storage.writeItem(1, nvs::ItemType::BLOB, "key2", blob, sizeof(blob)), ESP_ERR_NVS_NOT_ENOUGH_SPACE);
|
||||
TEST_ESP_ERR(storage.writeItem(1, nvs::ItemType::BLOB, "key2", blob, sizeof(blob), purgeAfterErase), ESP_ERR_NVS_NOT_ENOUGH_SPACE);
|
||||
|
||||
nvs::Page p;
|
||||
TEST_ESP_OK(p.load(&h, 3)); // This is where index will be placed.
|
||||
@@ -2365,7 +2378,7 @@ TEST_CASE("Check that orphaned blobs are erased during init", "[nvs]")
|
||||
TEST_ESP_OK(storage.init(0, required_sectors));
|
||||
|
||||
TEST_ESP_ERR(storage.readItem(1, nvs::ItemType::BLOB, "key", blob, sizeof(blob)), ESP_ERR_NVS_NOT_FOUND);
|
||||
TEST_ESP_OK(storage.writeItem(1, nvs::ItemType::BLOB, "key3", blob, sizeof(blob)));
|
||||
TEST_ESP_OK(storage.writeItem(1, nvs::ItemType::BLOB, "key3", blob, sizeof(blob), purgeAfterErase));
|
||||
}
|
||||
|
||||
TEST_CASE("nvs blob fragmentation test", "[nvs]")
|
||||
@@ -2434,6 +2447,7 @@ TEST_CASE("nvs code handles errors properly when partition is near to full", "[n
|
||||
|
||||
// Initialize NVS partition
|
||||
NVSPartitionTestHelper h(TEST_DEFAULT_PARTITION_NAME);
|
||||
const bool purgeAfterErase = TEST_DEFAULT_PURGE_AFTER_ERASE;
|
||||
|
||||
// This test requires at least 4+1 NVS pages to run.
|
||||
CHECK(h.get_sectors() >= 5);
|
||||
@@ -2449,12 +2463,12 @@ TEST_CASE("nvs code handles errors properly when partition is near to full", "[n
|
||||
// Four pages should fit roughly 12 blobs
|
||||
for (uint8_t count = 1; count <= 12; count++) {
|
||||
snprintf(nvs_key, sizeof(nvs_key), "key:%u", count);
|
||||
TEST_ESP_OK(storage.writeItem(1, nvs::ItemType::BLOB, nvs_key, blob, sizeof(blob)));
|
||||
TEST_ESP_OK(storage.writeItem(1, nvs::ItemType::BLOB, nvs_key, blob, sizeof(blob), purgeAfterErase));
|
||||
}
|
||||
|
||||
for (uint8_t count = 13; count <= 20; count++) {
|
||||
snprintf(nvs_key, sizeof(nvs_key), "key:%u", count);
|
||||
TEST_ESP_ERR(storage.writeItem(1, nvs::ItemType::BLOB, nvs_key, blob, sizeof(blob)), ESP_ERR_NVS_NOT_ENOUGH_SPACE);
|
||||
TEST_ESP_ERR(storage.writeItem(1, nvs::ItemType::BLOB, nvs_key, blob, sizeof(blob), purgeAfterErase), ESP_ERR_NVS_NOT_ENOUGH_SPACE);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2513,6 +2527,7 @@ TEST_CASE("monkey test with old-format blob present", "[nvs][monkey][xxx]")
|
||||
|
||||
// Clear partition stats before testing
|
||||
NVSPartitionTestHelper::clear_stats();
|
||||
const bool purgeAfterErase = true;
|
||||
|
||||
static const size_t smallBlobLen = nvs::Page::CHUNK_MAX_SIZE / 3;
|
||||
|
||||
@@ -2531,9 +2546,9 @@ TEST_CASE("monkey test with old-format blob present", "[nvs][monkey][xxx]")
|
||||
for (uint8_t num = 0; num < sector_count; num++) {
|
||||
nvs::Page p;
|
||||
TEST_ESP_OK(p.load(&h, num));
|
||||
p.eraseItem(1, nvs::ItemType::BLOB, "singlepage", nvs::Item::CHUNK_ANY, nvs::VerOffset::VER_ANY);
|
||||
p.eraseItem(1, nvs::ItemType::BLOB_IDX, "singlepage", nvs::Item::CHUNK_ANY, nvs::VerOffset::VER_ANY);
|
||||
p.eraseItem(1, nvs::ItemType::BLOB_DATA, "singlepage", nvs::Item::CHUNK_ANY, nvs::VerOffset::VER_ANY);
|
||||
p.eraseItem(1, nvs::ItemType::BLOB, "singlepage", purgeAfterErase, nvs::Item::CHUNK_ANY, nvs::VerOffset::VER_ANY);
|
||||
p.eraseItem(1, nvs::ItemType::BLOB_IDX, "singlepage", purgeAfterErase, nvs::Item::CHUNK_ANY, nvs::VerOffset::VER_ANY);
|
||||
p.eraseItem(1, nvs::ItemType::BLOB_DATA, "singlepage", purgeAfterErase, nvs::Item::CHUNK_ANY, nvs::VerOffset::VER_ANY);
|
||||
}
|
||||
|
||||
// Now write "singlepage" blob in old format
|
||||
@@ -3186,6 +3201,7 @@ TEST_CASE("recovery after failure to write data", "[nvs]")
|
||||
|
||||
// PartitionTestHelper is used to provide nvs::Partition instance for the test
|
||||
NVSPartitionTestHelper h(TEST_DEFAULT_PARTITION_NAME);
|
||||
const bool purgeAfterErase = TEST_DEFAULT_PURGE_AFTER_ERASE;
|
||||
|
||||
const char str[] = "value 0123456789abcdef012345678value 0123456789abcdef012345678";
|
||||
|
||||
@@ -3195,10 +3211,10 @@ TEST_CASE("recovery after failure to write data", "[nvs]")
|
||||
nvs::Storage storage(&h);
|
||||
TEST_ESP_OK(storage.init(0, 3));
|
||||
|
||||
TEST_ESP_ERR(storage.writeItem(1, nvs::ItemType::SZ, "key", str, strlen(str)), ESP_ERR_FLASH_OP_FAIL);
|
||||
TEST_ESP_ERR(storage.writeItem(1, nvs::ItemType::SZ, "key", str, strlen(str), purgeAfterErase), ESP_ERR_FLASH_OP_FAIL);
|
||||
|
||||
// check that repeated operations cause an error
|
||||
TEST_ESP_ERR(storage.writeItem(1, nvs::ItemType::SZ, "key", str, strlen(str)), ESP_ERR_NVS_INVALID_STATE);
|
||||
TEST_ESP_ERR(storage.writeItem(1, nvs::ItemType::SZ, "key", str, strlen(str), purgeAfterErase), ESP_ERR_NVS_INVALID_STATE);
|
||||
|
||||
uint8_t val;
|
||||
TEST_ESP_ERR(storage.readItem(1, nvs::ItemType::U8, "key", &val, sizeof(val)), ESP_ERR_NVS_NOT_FOUND);
|
||||
@@ -3226,13 +3242,14 @@ TEST_CASE("crc errors in item header are handled", "[nvs]")
|
||||
|
||||
// PartitionTestHelper is used to provide nvs::Partition instance for the test
|
||||
NVSPartitionTestHelper h(TEST_DEFAULT_PARTITION_NAME);
|
||||
const bool purgeAfterErase = TEST_DEFAULT_PURGE_AFTER_ERASE;
|
||||
|
||||
nvs::Storage storage(&h);
|
||||
// prepare some data
|
||||
TEST_ESP_OK(storage.init(0, 3));
|
||||
TEST_ESP_OK(storage.writeItem(0, "ns1", static_cast<uint8_t>(1)));
|
||||
TEST_ESP_OK(storage.writeItem(1, "value1", static_cast<uint32_t>(1)));
|
||||
TEST_ESP_OK(storage.writeItem(1, "value2", static_cast<uint32_t>(2)));
|
||||
TEST_ESP_OK(storage.writeItem(0, "ns1", static_cast<uint8_t>(1), purgeAfterErase));
|
||||
TEST_ESP_OK(storage.writeItem(1, "value1", static_cast<uint32_t>(1), purgeAfterErase));
|
||||
TEST_ESP_OK(storage.writeItem(1, "value2", static_cast<uint32_t>(2), purgeAfterErase));
|
||||
|
||||
// corrupt item header
|
||||
uint32_t val = 0;
|
||||
@@ -3249,7 +3266,7 @@ TEST_CASE("crc errors in item header are handled", "[nvs]")
|
||||
for (size_t i = 0; i < nvs::Page::ENTRY_COUNT; ++i) {
|
||||
char item_name[nvs::Item::MAX_KEY_LENGTH + 1];
|
||||
snprintf(item_name, sizeof(item_name), "item_%ld", (long int)i);
|
||||
TEST_ESP_OK(storage.writeItem(1, item_name, static_cast<uint32_t>(i)));
|
||||
TEST_ESP_OK(storage.writeItem(1, item_name, static_cast<uint32_t>(i), purgeAfterErase));
|
||||
}
|
||||
|
||||
// corrupt another item on the full page
|
||||
@@ -3316,13 +3333,14 @@ TEST_CASE("zero span in item header with correct crc is handled", "[nvs]")
|
||||
|
||||
// PartitionTestHelper is used to provide nvs::Partition instance for the test
|
||||
NVSPartitionTestHelper h(TEST_DEFAULT_PARTITION_NAME);
|
||||
const bool purgeAfterErase = TEST_DEFAULT_PURGE_AFTER_ERASE;
|
||||
|
||||
nvs::Storage storage(&h);
|
||||
// prepare some data
|
||||
TEST_ESP_OK(storage.init(0, 3));
|
||||
TEST_ESP_OK(storage.writeItem(0, "ns1", static_cast<uint8_t>(1)));
|
||||
TEST_ESP_OK(storage.writeItem(1, "value1", static_cast<uint32_t>(1)));
|
||||
TEST_ESP_OK(storage.writeItem(1, "value2", static_cast<uint32_t>(2)));
|
||||
TEST_ESP_OK(storage.writeItem(0, "ns1", static_cast<uint8_t>(1), purgeAfterErase));
|
||||
TEST_ESP_OK(storage.writeItem(1, "value1", static_cast<uint32_t>(1), purgeAfterErase));
|
||||
TEST_ESP_OK(storage.writeItem(1, "value2", static_cast<uint32_t>(2), purgeAfterErase));
|
||||
|
||||
// damage item header of value1 to introduce span==0 error, recalculate crc
|
||||
{
|
||||
@@ -3357,7 +3375,7 @@ TEST_CASE("zero span in item header with correct crc is handled", "[nvs]")
|
||||
for (size_t i = 0; i < nvs::Page::ENTRY_COUNT; ++i) {
|
||||
char item_name[nvs::Item::MAX_KEY_LENGTH + 1];
|
||||
snprintf(item_name, sizeof(item_name), "item_%ld", (long int)i);
|
||||
TEST_ESP_OK(storage.writeItem(1, item_name, static_cast<uint32_t>(i)));
|
||||
TEST_ESP_OK(storage.writeItem(1, item_name, static_cast<uint32_t>(i), purgeAfterErase));
|
||||
}
|
||||
|
||||
// damage item header of item_125 to introduce span==0 error, recalculate crc
|
||||
@@ -3397,18 +3415,19 @@ TEST_CASE("inconsistent fields in item header with correct crc are handled for s
|
||||
|
||||
// PartitionTestHelper is used to provide nvs::Partition instance for the test
|
||||
NVSPartitionTestHelper h(TEST_DEFAULT_PARTITION_NAME);
|
||||
const bool purgeAfterErase = TEST_DEFAULT_PURGE_AFTER_ERASE;
|
||||
|
||||
nvs::Storage storage(&h);
|
||||
// prepare some data
|
||||
TEST_ESP_OK(storage.init(0, 3));
|
||||
TEST_ESP_OK(storage.writeItem(0, "ns1", static_cast<uint8_t>(1)));
|
||||
TEST_ESP_OK(storage.writeItem(0, "ns1", static_cast<uint8_t>(1), purgeAfterErase));
|
||||
const char str[] = "String67890123456789012345678901String67890123456789012345678901String6789012345678901234567890"; // 95 + 1 bytes data occupy 3 entries, overhead 1
|
||||
|
||||
TEST_ESP_OK(storage.writeItem(1, nvs::ItemType::SZ, "valuestr1", str, strlen(str)));
|
||||
TEST_ESP_OK(storage.writeItem(1, nvs::ItemType::SZ, "valuestr2", str, strlen(str)));
|
||||
TEST_ESP_OK(storage.writeItem(1, nvs::ItemType::SZ, "valuestr3", str, strlen(str)));
|
||||
TEST_ESP_OK(storage.writeItem(1, nvs::ItemType::SZ, "valuestr4", str, strlen(str)));
|
||||
TEST_ESP_OK(storage.writeItem(1, "valueu32", static_cast<uint32_t>(2)));
|
||||
TEST_ESP_OK(storage.writeItem(1, nvs::ItemType::SZ, "valuestr1", str, strlen(str), purgeAfterErase));
|
||||
TEST_ESP_OK(storage.writeItem(1, nvs::ItemType::SZ, "valuestr2", str, strlen(str), purgeAfterErase));
|
||||
TEST_ESP_OK(storage.writeItem(1, nvs::ItemType::SZ, "valuestr3", str, strlen(str), purgeAfterErase));
|
||||
TEST_ESP_OK(storage.writeItem(1, nvs::ItemType::SZ, "valuestr4", str, strlen(str), purgeAfterErase));
|
||||
TEST_ESP_OK(storage.writeItem(1, "valueu32", static_cast<uint32_t>(2), purgeAfterErase));
|
||||
|
||||
// read the values back
|
||||
char read_str[sizeof(str)] = {0};
|
||||
@@ -3789,14 +3808,15 @@ TEST_CASE("invalid data type in item header with correct crc is handled", "[nvs]
|
||||
|
||||
// PartitionTestHelper is used to provide nvs::Partition instance for the test
|
||||
NVSPartitionTestHelper h(TEST_DEFAULT_PARTITION_NAME);
|
||||
const bool purgeAfterErase = TEST_DEFAULT_PURGE_AFTER_ERASE;
|
||||
|
||||
nvs::Storage storage(&h);
|
||||
// prepare some data
|
||||
TEST_ESP_OK(storage.init(0, 3));
|
||||
TEST_ESP_OK(storage.writeItem(0, "ns1", static_cast<uint8_t>(1)));
|
||||
TEST_ESP_OK(storage.writeItem(1, "value1", static_cast<uint32_t>(1)));
|
||||
TEST_ESP_OK(storage.writeItem(1, "value2", static_cast<uint32_t>(2)));
|
||||
TEST_ESP_OK(storage.writeItem(1, "value3", static_cast<uint32_t>(3)));
|
||||
TEST_ESP_OK(storage.writeItem(0, "ns1", static_cast<uint8_t>(1), purgeAfterErase));
|
||||
TEST_ESP_OK(storage.writeItem(1, "value1", static_cast<uint32_t>(1), purgeAfterErase));
|
||||
TEST_ESP_OK(storage.writeItem(1, "value2", static_cast<uint32_t>(2), purgeAfterErase));
|
||||
TEST_ESP_OK(storage.writeItem(1, "value3", static_cast<uint32_t>(3), purgeAfterErase));
|
||||
|
||||
// damage item header of value2 to introduce data type error, recalculate crc
|
||||
{
|
||||
@@ -4745,6 +4765,249 @@ TEST_CASE("nvs find key tests", "[nvs]")
|
||||
nvs_close(handle_2);
|
||||
TEST_ESP_OK(nvs_flash_deinit_partition(TEST_DEFAULT_PARTITION_NAME));
|
||||
}
|
||||
|
||||
TEST_CASE("nvs handle purge test", "[nvs]")
|
||||
{
|
||||
// TC validating that erased and overwritten entries are purged from NVS storage based on the mode of handle opening.
|
||||
// The test covers both modes: with implicit purge enabled and disabled.
|
||||
// To validate the behavior, the test:
|
||||
// Writes entries of different types to NVS storage. Entries include: integer, string and multi page blob.
|
||||
// Inf first round, the data contains initial markers to identify them.
|
||||
// Then it erases / overwrites the entries one by one and after each erase it checks whether the markers are still present in the storage
|
||||
|
||||
// SECTIONS:
|
||||
// 1. Open writable namespace without implicit purge. Erase data. Should find markers.
|
||||
// 2/ Open writable namespace without implicit purge. Overwrite data. Should find old markers.
|
||||
// 3. Open writable namespace with implicit purge. Erase data. Should NOT find markers.
|
||||
// 4. Open writable namespace with implicit purge. Overwrite data. Should NOT find old markers.
|
||||
|
||||
const char* part_name = TEST_DEFAULT_PARTITION_NAME;
|
||||
const char* ns_name = "ns1";
|
||||
|
||||
|
||||
// Markers and lengths used in the test
|
||||
// String mrkers are fixed strings at the beginning of the string data as well as at the end of the string data
|
||||
const char str_key[] = "str_key";
|
||||
const char str_marker_start[] = "STR_START_";;
|
||||
const char str_marker_end[] = "_STR_END";
|
||||
const size_t string_len = 3000; // 4000 is max, 3000 fills almost entire page
|
||||
|
||||
// Blob markers are fixed patterns at the beginning of the blob data as well as at the end of the blob data
|
||||
const char blob_key[] = "blob_key";
|
||||
const uint8_t blob_marker_start[] = {0xBA, 0xAD, 0xF0, 0x0D};
|
||||
const uint8_t blob_marker_end[] = {0xD0, 0x0F, 0xDA, 0xAB};
|
||||
const size_t blob_len = 8192; // 8192 bytes to ensure multipage blob
|
||||
|
||||
// uint64_t marker is used for integer data
|
||||
const char uint_key[] = "uint_key";
|
||||
const uint64_t uint_marker = 0xDEADBEEFFEEBDAED;
|
||||
|
||||
// Overwrite patterns
|
||||
const char str_overwrite_pattern = 'X';
|
||||
const uint8_t blob_overwrite_pattern = 0x5A;
|
||||
const uint64_t uint_overwrite_pattern = 0xA5A5A5A5A5A5A5A5;
|
||||
|
||||
|
||||
// nvs_flash_erase_partition to start with clean partition
|
||||
TEST_ESP_OK(nvs_flash_erase_partition(part_name));
|
||||
TEST_ESP_OK(nvs_flash_init_partition(part_name));
|
||||
|
||||
// init non-purge handle
|
||||
nvs_handle_t handle;
|
||||
TEST_ESP_OK(nvs_open_from_partition(part_name, ns_name, NVS_READWRITE, &handle));
|
||||
|
||||
// populate with markers
|
||||
// string
|
||||
char* str_data = (char*) malloc(string_len);
|
||||
CHECK(str_data != nullptr);
|
||||
memset(str_data, 'A', string_len);
|
||||
memcpy(str_data, str_marker_start, strlen(str_marker_start));
|
||||
memcpy(str_data + string_len - (strlen(str_marker_end) + 1), str_marker_end, strlen(str_marker_end));
|
||||
str_data[string_len - 1] = '\0'; // ensure null termination
|
||||
TEST_ESP_OK(nvs_set_str(handle, str_key, str_data));
|
||||
free(str_data);
|
||||
|
||||
// blob
|
||||
uint8_t* blob_data = (uint8_t*) malloc(blob_len);
|
||||
CHECK(blob_data != nullptr);
|
||||
memset(blob_data, 0xFF, blob_len);
|
||||
memcpy(blob_data, blob_marker_start, sizeof(blob_marker_start));
|
||||
memcpy(blob_data + blob_len - sizeof(blob_marker_end), blob_marker_end, sizeof(blob_marker_end));
|
||||
TEST_ESP_OK(nvs_set_blob(handle, blob_key, blob_data, blob_len));
|
||||
free(blob_data);
|
||||
|
||||
// uint64_t
|
||||
TEST_ESP_OK(nvs_set_u64(handle, uint_key, uint_marker));
|
||||
TEST_ESP_OK(nvs_commit(handle));
|
||||
|
||||
// make sure markers are written
|
||||
// check string marker
|
||||
TEST_ESP_OK(NVSPartitionTestHelper::check_marker(part_name, (const uint8_t*)str_marker_start, strlen(str_marker_start)));
|
||||
TEST_ESP_OK(NVSPartitionTestHelper::check_marker(part_name, (const uint8_t*)str_marker_end, strlen(str_marker_end)));
|
||||
// check blob marker
|
||||
TEST_ESP_OK(NVSPartitionTestHelper::check_marker(part_name, blob_marker_start, sizeof(blob_marker_start)));
|
||||
TEST_ESP_OK(NVSPartitionTestHelper::check_marker(part_name, blob_marker_end, sizeof(blob_marker_end)));
|
||||
// check uint64_t marker
|
||||
TEST_ESP_OK(NVSPartitionTestHelper::check_marker(part_name, (const uint8_t*)&uint_marker, sizeof(uint_marker)));
|
||||
|
||||
// deinit
|
||||
nvs_close(handle);
|
||||
|
||||
SECTION("non-purge handle - erase entries")
|
||||
{
|
||||
// init non-purge handle
|
||||
TEST_ESP_OK(nvs_open_from_partition(part_name, ns_name, NVS_READWRITE, &handle));
|
||||
|
||||
// erase / check markers
|
||||
TEST_ESP_OK(nvs_erase_key(handle, str_key));
|
||||
TEST_ESP_OK(nvs_erase_key(handle, blob_key));
|
||||
TEST_ESP_OK(nvs_erase_key(handle, uint_key));
|
||||
|
||||
TEST_ESP_OK(nvs_commit(handle));
|
||||
|
||||
// this is non implicit purge handle, so markers should be found
|
||||
// check string marker
|
||||
TEST_ESP_OK(NVSPartitionTestHelper::check_marker(part_name, (const uint8_t*)str_marker_start, strlen(str_marker_start)));
|
||||
TEST_ESP_OK(NVSPartitionTestHelper::check_marker(part_name, (const uint8_t*)str_marker_end, strlen(str_marker_end)));
|
||||
// check blob marker
|
||||
TEST_ESP_OK(NVSPartitionTestHelper::check_marker(part_name, blob_marker_start, sizeof(blob_marker_start)));
|
||||
TEST_ESP_OK(NVSPartitionTestHelper::check_marker(part_name, blob_marker_end, sizeof(blob_marker_end)));
|
||||
// check uint64_t marker
|
||||
TEST_ESP_OK(NVSPartitionTestHelper::check_marker(part_name, (const uint8_t*)&uint_marker, sizeof(uint_marker)));
|
||||
|
||||
// explicitly call purge
|
||||
TEST_ESP_OK(nvs_purge_all(handle));
|
||||
|
||||
// after purge, markers should NOT be found
|
||||
// check string marker
|
||||
TEST_ESP_ERR(NVSPartitionTestHelper::check_marker(part_name, (const uint8_t*)str_marker_start, strlen(str_marker_start)), ESP_ERR_NVS_NOT_FOUND);
|
||||
TEST_ESP_ERR(NVSPartitionTestHelper::check_marker(part_name, (const uint8_t*)str_marker_end, strlen(str_marker_end)), ESP_ERR_NVS_NOT_FOUND);
|
||||
// check blob marker
|
||||
TEST_ESP_ERR(NVSPartitionTestHelper::check_marker(part_name, blob_marker_start, sizeof(blob_marker_start)), ESP_ERR_NVS_NOT_FOUND);
|
||||
TEST_ESP_ERR(NVSPartitionTestHelper::check_marker(part_name, blob_marker_end, sizeof(blob_marker_end)), ESP_ERR_NVS_NOT_FOUND);
|
||||
// check uint64_t marker
|
||||
TEST_ESP_ERR(NVSPartitionTestHelper::check_marker(part_name, (const uint8_t*)&uint_marker, sizeof(uint_marker)), ESP_ERR_NVS_NOT_FOUND);
|
||||
}
|
||||
|
||||
SECTION("non-purge handle - overwrite entries")
|
||||
{
|
||||
// init non-purge handle
|
||||
TEST_ESP_OK(nvs_open_from_partition(part_name, ns_name, NVS_READWRITE, &handle));
|
||||
|
||||
// overwrite / check markers
|
||||
// string
|
||||
char* str_data_ov = (char*) malloc(string_len);
|
||||
CHECK(str_data_ov != nullptr);
|
||||
memset(str_data_ov, str_overwrite_pattern, string_len);
|
||||
str_data_ov[string_len - 1] = '\0'; // ensure null termination
|
||||
TEST_ESP_OK(nvs_set_str(handle, str_key, str_data_ov));
|
||||
|
||||
// blob
|
||||
uint8_t* blob_data_ov = (uint8_t*) malloc(blob_len);
|
||||
CHECK(blob_data_ov != nullptr);
|
||||
memset(blob_data_ov, blob_overwrite_pattern, blob_len);
|
||||
TEST_ESP_OK(nvs_set_blob(handle, blob_key, blob_data_ov, blob_len));
|
||||
|
||||
// uint64_t
|
||||
TEST_ESP_OK(nvs_set_u64(handle, uint_key, uint_overwrite_pattern));
|
||||
TEST_ESP_OK(nvs_commit(handle));
|
||||
|
||||
free(str_data_ov);
|
||||
free(blob_data_ov);
|
||||
|
||||
// this is non implicit purge handle, so old markers should be found
|
||||
// check string marker
|
||||
TEST_ESP_OK(NVSPartitionTestHelper::check_marker(part_name, (const uint8_t*)str_marker_start, strlen(str_marker_start)));
|
||||
TEST_ESP_OK(NVSPartitionTestHelper::check_marker(part_name, (const uint8_t*)str_marker_end, strlen(str_marker_end)));
|
||||
// check blob marker
|
||||
TEST_ESP_OK(NVSPartitionTestHelper::check_marker(part_name, blob_marker_start, sizeof(blob_marker_start)));
|
||||
TEST_ESP_OK(NVSPartitionTestHelper::check_marker(part_name, blob_marker_end, sizeof(blob_marker_end)));
|
||||
// check uint64_t marker
|
||||
TEST_ESP_OK(NVSPartitionTestHelper::check_marker(part_name, (const uint8_t*)&uint_marker, sizeof(uint_marker)));
|
||||
|
||||
// explicitly call purge
|
||||
TEST_ESP_OK(nvs_purge_all(handle));
|
||||
|
||||
// after purge, markers should NOT be found
|
||||
// check string marker
|
||||
TEST_ESP_ERR(NVSPartitionTestHelper::check_marker(part_name, (const uint8_t*)str_marker_start, strlen(str_marker_start)), ESP_ERR_NVS_NOT_FOUND);
|
||||
TEST_ESP_ERR(NVSPartitionTestHelper::check_marker(part_name, (const uint8_t*)str_marker_end, strlen(str_marker_end)), ESP_ERR_NVS_NOT_FOUND);
|
||||
// check blob marker
|
||||
TEST_ESP_ERR(NVSPartitionTestHelper::check_marker(part_name, blob_marker_start, sizeof(blob_marker_start)), ESP_ERR_NVS_NOT_FOUND);
|
||||
TEST_ESP_ERR(NVSPartitionTestHelper::check_marker(part_name, blob_marker_end, sizeof(blob_marker_end)), ESP_ERR_NVS_NOT_FOUND);
|
||||
// check uint64_t marker
|
||||
TEST_ESP_ERR(NVSPartitionTestHelper::check_marker(part_name, (const uint8_t*)&uint_marker, sizeof(uint_marker)), ESP_ERR_NVS_NOT_FOUND);
|
||||
}
|
||||
|
||||
SECTION("purge handle - erase entries")
|
||||
{
|
||||
// init purge handle
|
||||
TEST_ESP_OK(nvs_open_from_partition(part_name, ns_name, NVS_READWRITE_PURGE, &handle));
|
||||
|
||||
// erase / check markers
|
||||
TEST_ESP_OK(nvs_erase_key(handle, str_key));
|
||||
TEST_ESP_OK(nvs_erase_key(handle, blob_key));
|
||||
TEST_ESP_OK(nvs_erase_key(handle, uint_key));
|
||||
|
||||
TEST_ESP_OK(nvs_commit(handle));
|
||||
|
||||
// this is purge handle, so markers should NOT be found
|
||||
// check string marker
|
||||
TEST_ESP_ERR(NVSPartitionTestHelper::check_marker(part_name, (const uint8_t*)str_marker_start, strlen(str_marker_start)), ESP_ERR_NVS_NOT_FOUND);
|
||||
TEST_ESP_ERR(NVSPartitionTestHelper::check_marker(part_name, (const uint8_t*)str_marker_end, strlen(str_marker_end)), ESP_ERR_NVS_NOT_FOUND);
|
||||
// check blob marker
|
||||
TEST_ESP_ERR(NVSPartitionTestHelper::check_marker(part_name, blob_marker_start, sizeof(blob_marker_start)), ESP_ERR_NVS_NOT_FOUND);
|
||||
TEST_ESP_ERR(NVSPartitionTestHelper::check_marker(part_name, blob_marker_end, sizeof(blob_marker_end)), ESP_ERR_NVS_NOT_FOUND);
|
||||
// check uint64_t marker
|
||||
TEST_ESP_ERR(NVSPartitionTestHelper::check_marker(part_name, (const uint8_t*)&uint_marker, sizeof(uint_marker)), ESP_ERR_NVS_NOT_FOUND);
|
||||
}
|
||||
|
||||
SECTION("purge handle - overwrite entries")
|
||||
{
|
||||
// init purge handle
|
||||
TEST_ESP_OK(nvs_open_from_partition(part_name, ns_name, NVS_READWRITE_PURGE, &handle));
|
||||
|
||||
// overwrite / check markers
|
||||
// string
|
||||
char* str_data_ov = (char*) malloc(string_len);
|
||||
CHECK(str_data_ov != nullptr);
|
||||
memset(str_data_ov, str_overwrite_pattern, string_len);
|
||||
str_data_ov[string_len - 1] = '\0'; // ensure null termination
|
||||
|
||||
TEST_ESP_OK(nvs_set_str(handle, str_key, str_data_ov));
|
||||
|
||||
// blob
|
||||
uint8_t* blob_data_ov = (uint8_t*) malloc(blob_len);
|
||||
CHECK(blob_data_ov != nullptr);
|
||||
memset(blob_data_ov, blob_overwrite_pattern, blob_len);
|
||||
TEST_ESP_OK(nvs_set_blob(handle, blob_key, blob_data_ov, blob_len));
|
||||
|
||||
// uint64_t
|
||||
TEST_ESP_OK(nvs_set_u64(handle, uint_key, uint_overwrite_pattern));
|
||||
TEST_ESP_OK(nvs_commit(handle));
|
||||
|
||||
free(str_data_ov);
|
||||
free(blob_data_ov);
|
||||
|
||||
// this is purge handle, so old markers should NOT be found
|
||||
// check string marker
|
||||
TEST_ESP_ERR(NVSPartitionTestHelper::check_marker(part_name, (const uint8_t*)str_marker_start, strlen(str_marker_start)), ESP_ERR_NVS_NOT_FOUND);
|
||||
TEST_ESP_ERR(NVSPartitionTestHelper::check_marker(part_name, (const uint8_t*)str_marker_end, strlen(str_marker_end)), ESP_ERR_NVS_NOT_FOUND);
|
||||
// check blob marker
|
||||
TEST_ESP_ERR(NVSPartitionTestHelper::check_marker(part_name, blob_marker_start, sizeof(blob_marker_start)), ESP_ERR_NVS_NOT_FOUND);
|
||||
TEST_ESP_ERR(NVSPartitionTestHelper::check_marker(part_name, blob_marker_end, sizeof(blob_marker_end)), ESP_ERR_NVS_NOT_FOUND);
|
||||
// check uint64_t marker
|
||||
TEST_ESP_ERR(NVSPartitionTestHelper::check_marker(part_name, (const uint8_t*)&uint_marker, sizeof(uint_marker)), ESP_ERR_NVS_NOT_FOUND);
|
||||
}
|
||||
|
||||
// deinit
|
||||
nvs_close(handle);
|
||||
|
||||
// clean up partition
|
||||
TEST_ESP_OK(nvs_flash_erase_partition(part_name));
|
||||
}
|
||||
|
||||
|
||||
// Add new tests above
|
||||
// This test has to be the final one
|
||||
|
||||
|
||||
@@ -12,6 +12,7 @@
|
||||
using namespace std;
|
||||
|
||||
#define TEST_DEFAULT_PARTITION_NAME "nvs"
|
||||
#define TEST_DEFAULT_PURGE_AFTER_ERASE true // erase with purge after erase
|
||||
|
||||
TEST_CASE("Storage iterator recognizes blob with VerOffset::VER_1_OFFSET", "[nvs_storage]")
|
||||
{
|
||||
@@ -31,11 +32,10 @@ TEST_CASE("Storage iterator recognizes blob with VerOffset::VER_1_OFFSET", "[nvs
|
||||
REQUIRE(storage != nullptr);
|
||||
storage->createOrOpenNamespace("test_ns", true, ns_index);
|
||||
|
||||
CHECK(storage->writeItem(ns_index, nvs::ItemType::BLOB, "test_blob", blob, sizeof(blob)) == ESP_OK);
|
||||
CHECK(storage->writeItem(ns_index, nvs::ItemType::BLOB, "test_blob", blob, sizeof(blob), TEST_DEFAULT_PURGE_AFTER_ERASE) == ESP_OK);
|
||||
|
||||
// changing provokes a blob with version offset 1 (VerOffset::VER_1_OFFSET)
|
||||
CHECK(storage->writeItem(ns_index, nvs::ItemType::BLOB, "test_blob", blob_new, sizeof(blob_new)) == ESP_OK);
|
||||
|
||||
CHECK(storage->writeItem(ns_index, nvs::ItemType::BLOB, "test_blob", blob_new, sizeof(blob_new), TEST_DEFAULT_PURGE_AFTER_ERASE) == ESP_OK);
|
||||
nvs_opaque_iterator_t it;
|
||||
it.storage = storage;
|
||||
it.type = NVS_TYPE_ANY;
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2015-2024 Espressif Systems (Shanghai) CO LTD
|
||||
* SPDX-FileCopyrightText: 2015-2025 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
@@ -26,6 +26,8 @@ NVS_GUARD_SYSVIEW_MACRO_EXPANSION_PUSH();
|
||||
using namespace std;
|
||||
using namespace nvs;
|
||||
|
||||
#define DEFAULT_PURGE_AFTER_ERASE false
|
||||
|
||||
void test_Page_load_reading_header_fails()
|
||||
{
|
||||
PartitionEmulationFixture fix;
|
||||
@@ -661,7 +663,7 @@ void test_Page_eraseItem__uninitialized()
|
||||
{
|
||||
Page page;
|
||||
|
||||
TEST_ASSERT_EQUAL(ESP_ERR_NVS_NOT_FOUND, page.eraseItem<uint8_t>(NVSValidPageFixture::NS_INDEX, "test_value"));
|
||||
TEST_ASSERT_EQUAL(ESP_ERR_NVS_NOT_FOUND, page.eraseItem<uint8_t>(NVSValidPageFixture::NS_INDEX, "test_value", DEFAULT_PURGE_AFTER_ERASE));
|
||||
}
|
||||
|
||||
void test_Page_eraseItem__key_not_found()
|
||||
@@ -672,7 +674,7 @@ void test_Page_eraseItem__key_not_found()
|
||||
|
||||
TEST_ASSERT_EQUAL(Page::PageState::ACTIVE, fix.page.state());
|
||||
|
||||
TEST_ASSERT_EQUAL(ESP_ERR_NVS_NOT_FOUND, fix.page.eraseItem<uint8_t>(NVSValidPageFixture::NS_INDEX, "different"));
|
||||
TEST_ASSERT_EQUAL(ESP_ERR_NVS_NOT_FOUND, fix.page.eraseItem<uint8_t>(NVSValidPageFixture::NS_INDEX, "different", DEFAULT_PURGE_AFTER_ERASE));
|
||||
|
||||
TEST_ASSERT_EQUAL(2, fix.page.getUsedEntryCount());
|
||||
TEST_ASSERT_EQUAL(122, fix.page.getErasedEntryCount());
|
||||
@@ -692,7 +694,7 @@ void test_Page_eraseItem__write_fail()
|
||||
// simulated write failure should fail the eraseItem call
|
||||
fix.fail_write_at(1);
|
||||
|
||||
TEST_ASSERT_EQUAL(ESP_ERR_FLASH_OP_FAIL, fix.page.eraseItem<uint8_t>(NVSValidPageFixture::NS_INDEX, "test_value"));
|
||||
TEST_ASSERT_EQUAL(ESP_ERR_FLASH_OP_FAIL, fix.page.eraseItem<uint8_t>(NVSValidPageFixture::NS_INDEX, "test_value", DEFAULT_PURGE_AFTER_ERASE));
|
||||
|
||||
TEST_ASSERT_EQUAL(1, fix.page.getUsedEntryCount());
|
||||
TEST_ASSERT_EQUAL(123, fix.page.getErasedEntryCount());
|
||||
@@ -706,7 +708,7 @@ void test_Page_eraseItem__write_succeed()
|
||||
TEST_ASSERT_EQUAL(2, fix.page.getUsedEntryCount());
|
||||
TEST_ASSERT_EQUAL(122, fix.page.getErasedEntryCount());
|
||||
TEST_ASSERT_EQUAL(Page::PageState::ACTIVE, fix.page.state());
|
||||
TEST_ASSERT_EQUAL(ESP_OK, fix.page.eraseItem<uint8_t>(NVSValidPageFixture::NS_INDEX, "test_value"));
|
||||
TEST_ASSERT_EQUAL(ESP_OK, fix.page.eraseItem<uint8_t>(NVSValidPageFixture::NS_INDEX, "test_value", DEFAULT_PURGE_AFTER_ERASE));
|
||||
TEST_ASSERT_EQUAL(1, fix.page.getUsedEntryCount());
|
||||
TEST_ASSERT_EQUAL(123, fix.page.getErasedEntryCount());
|
||||
TEST_ASSERT_EQUAL(Page::PageState::ACTIVE, fix.page.state());
|
||||
|
||||
@@ -84,8 +84,9 @@ _Pragma("pop_macro(\"I64\")")
|
||||
* @brief Mode of opening the non-volatile storage
|
||||
*/
|
||||
typedef enum {
|
||||
NVS_READONLY, /*!< Read only */
|
||||
NVS_READWRITE /*!< Read and write */
|
||||
NVS_READONLY, /*!< Read only */
|
||||
NVS_READWRITE, /*!< Read and write */
|
||||
NVS_READWRITE_PURGE /*!< Read and write */
|
||||
} nvs_open_mode_t;
|
||||
|
||||
/*
|
||||
@@ -136,9 +137,12 @@ typedef struct nvs_opaque_iterator_t *nvs_iterator_t;
|
||||
* table.
|
||||
*
|
||||
* @param[in] namespace_name Namespace name. Maximum length is (NVS_KEY_NAME_MAX_SIZE-1) characters. Shouldn't be empty.
|
||||
* @param[in] open_mode NVS_READWRITE or NVS_READONLY. If NVS_READONLY, will
|
||||
* open a handle for reading only. All write requests will
|
||||
* be rejected for this handle.
|
||||
* @param[in] open_mode NVS_READONLY opens a read only handle
|
||||
* NVS_READWRITE opens a read/write handle. erase and set operations are allowed.
|
||||
* previous data is marked as deleted only and new data is written to a new location.
|
||||
* NVS_READWRITE_PURGE opens a read/write handle. Update and erase operations are allowed.
|
||||
* previous data is purged from flash memory to ensure that it cannot be recovered.
|
||||
* New data is written to a new location.
|
||||
* @param[out] out_handle If successful (return code is zero), handle will be
|
||||
* returned in this argument.
|
||||
*
|
||||
@@ -558,6 +562,24 @@ esp_err_t nvs_erase_key(nvs_handle_t handle, const char* key);
|
||||
*/
|
||||
esp_err_t nvs_erase_all(nvs_handle_t handle);
|
||||
|
||||
/**
|
||||
* @brief Purge data of all erased key-value pairs in a namespace
|
||||
*
|
||||
* Note that actual storage may not be updated until nvs_commit function is called.
|
||||
*
|
||||
* @param[in] handle Storage handle obtained with nvs_open.
|
||||
* Handles that were opened read only cannot be used.
|
||||
*
|
||||
* @return
|
||||
* - ESP_OK if erase operation was successful
|
||||
* - ESP_FAIL if there is an internal error; most likely due to corrupted
|
||||
* NVS partition (only if NVS assertion checks are disabled)
|
||||
* - ESP_ERR_NVS_INVALID_HANDLE if handle has been closed or is NULL
|
||||
* - ESP_ERR_NVS_READ_ONLY if handle was opened as read only
|
||||
* - other error codes from the underlying storage driver
|
||||
*/
|
||||
esp_err_t nvs_purge_all(nvs_handle_t handle);
|
||||
|
||||
/**
|
||||
* @brief Write any pending changes to non-volatile storage
|
||||
*
|
||||
|
||||
@@ -210,6 +210,11 @@ public:
|
||||
*/
|
||||
virtual esp_err_t erase_all() = 0;
|
||||
|
||||
/**
|
||||
* Purges all erased entries in the scope of this handle. The scope may vary, depending on the implementation.
|
||||
*/
|
||||
virtual esp_err_t purge_all() = 0;
|
||||
|
||||
/**
|
||||
* Commits all changes done through this handle so far.
|
||||
* Currently, NVS writes to storage right after the set and get functions,
|
||||
|
||||
@@ -381,6 +381,20 @@ extern "C" esp_err_t nvs_erase_all(nvs_handle_t c_handle)
|
||||
return handle->erase_all();
|
||||
}
|
||||
|
||||
extern "C" esp_err_t nvs_purge_all(nvs_handle_t c_handle)
|
||||
{
|
||||
Lock lock;
|
||||
ESP_LOGD(TAG, "%s", __func__);
|
||||
NVSHandleSimple *handle;
|
||||
auto err = nvs_find_ns_handle(c_handle, &handle);
|
||||
if (err != ESP_OK) {
|
||||
return err;
|
||||
}
|
||||
|
||||
return handle->purge_all();
|
||||
}
|
||||
|
||||
|
||||
template<typename T>
|
||||
static esp_err_t nvs_set(nvs_handle_t c_handle, const char* key, T value)
|
||||
{
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2015-2023 Espressif Systems (Shanghai) CO LTD
|
||||
* SPDX-FileCopyrightText: 2015-2025 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
@@ -55,6 +55,11 @@ esp_err_t NVSHandleLocked::erase_all() {
|
||||
return handle->erase_all();
|
||||
}
|
||||
|
||||
esp_err_t NVSHandleLocked::purge_all() {
|
||||
Lock lock;
|
||||
return handle->purge_all();
|
||||
}
|
||||
|
||||
esp_err_t NVSHandleLocked::commit() {
|
||||
Lock lock;
|
||||
return handle->commit();
|
||||
|
||||
@@ -46,6 +46,8 @@ public:
|
||||
|
||||
esp_err_t erase_all() override;
|
||||
|
||||
esp_err_t purge_all() override;
|
||||
|
||||
esp_err_t commit() override;
|
||||
|
||||
esp_err_t get_used_entry_count(size_t& usedEntries) override;
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2015-2023 Espressif Systems (Shanghai) CO LTD
|
||||
* SPDX-FileCopyrightText: 2015-2025 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
@@ -18,7 +18,7 @@ esp_err_t NVSHandleSimple::set_typed_item(ItemType datatype, const char *key, co
|
||||
if (!valid) return ESP_ERR_NVS_INVALID_HANDLE;
|
||||
if (mReadOnly) return ESP_ERR_NVS_READ_ONLY;
|
||||
|
||||
return mStoragePtr->writeItem(mNsIndex, datatype, key, data, dataSize);
|
||||
return mStoragePtr->writeItem(mNsIndex, datatype, key, data, dataSize, mPurgeAfterErase);
|
||||
}
|
||||
|
||||
esp_err_t NVSHandleSimple::get_typed_item(ItemType datatype, const char *key, void* data, size_t dataSize)
|
||||
@@ -33,7 +33,7 @@ esp_err_t NVSHandleSimple::set_string(const char *key, const char* str)
|
||||
if (!valid) return ESP_ERR_NVS_INVALID_HANDLE;
|
||||
if (mReadOnly) return ESP_ERR_NVS_READ_ONLY;
|
||||
|
||||
return mStoragePtr->writeItem(mNsIndex, nvs::ItemType::SZ, key, str, strlen(str) + 1);
|
||||
return mStoragePtr->writeItem(mNsIndex, nvs::ItemType::SZ, key, str, strlen(str) + 1, mPurgeAfterErase);
|
||||
}
|
||||
|
||||
esp_err_t NVSHandleSimple::set_blob(const char *key, const void* blob, size_t len)
|
||||
@@ -41,7 +41,7 @@ esp_err_t NVSHandleSimple::set_blob(const char *key, const void* blob, size_t le
|
||||
if (!valid) return ESP_ERR_NVS_INVALID_HANDLE;
|
||||
if (mReadOnly) return ESP_ERR_NVS_READ_ONLY;
|
||||
|
||||
return mStoragePtr->writeItem(mNsIndex, nvs::ItemType::BLOB, key, blob, len);
|
||||
return mStoragePtr->writeItem(mNsIndex, nvs::ItemType::BLOB, key, blob, len, mPurgeAfterErase);
|
||||
}
|
||||
|
||||
esp_err_t NVSHandleSimple::get_string(const char *key, char* out_str, size_t len)
|
||||
@@ -87,7 +87,7 @@ esp_err_t NVSHandleSimple::erase_item(const char* key)
|
||||
if (!valid) return ESP_ERR_NVS_INVALID_HANDLE;
|
||||
if (mReadOnly) return ESP_ERR_NVS_READ_ONLY;
|
||||
|
||||
return mStoragePtr->eraseItem(mNsIndex, key);
|
||||
return mStoragePtr->eraseItem(mNsIndex, key, mPurgeAfterErase);
|
||||
}
|
||||
|
||||
esp_err_t NVSHandleSimple::erase_all()
|
||||
@@ -95,7 +95,15 @@ esp_err_t NVSHandleSimple::erase_all()
|
||||
if (!valid) return ESP_ERR_NVS_INVALID_HANDLE;
|
||||
if (mReadOnly) return ESP_ERR_NVS_READ_ONLY;
|
||||
|
||||
return mStoragePtr->eraseNamespace(mNsIndex);
|
||||
return mStoragePtr->eraseNamespace(mNsIndex, mPurgeAfterErase);
|
||||
}
|
||||
|
||||
esp_err_t NVSHandleSimple::purge_all()
|
||||
{
|
||||
if (!valid) return ESP_ERR_NVS_INVALID_HANDLE;
|
||||
if (mReadOnly) return ESP_ERR_NVS_READ_ONLY;
|
||||
|
||||
return mStoragePtr->purgeNamespace(mNsIndex);
|
||||
}
|
||||
|
||||
esp_err_t NVSHandleSimple::commit()
|
||||
|
||||
@@ -27,10 +27,11 @@ class NVSHandleSimple : public intrusive_list_node<NVSHandleSimple>,
|
||||
public ExceptionlessAllocatable {
|
||||
friend class NVSPartitionManager;
|
||||
public:
|
||||
NVSHandleSimple(bool readOnly, uint8_t nsIndex, Storage *StoragePtr) :
|
||||
NVSHandleSimple(bool readOnly, uint8_t nsIndex, Storage *StoragePtr, bool purgeAfterErase) :
|
||||
mStoragePtr(StoragePtr),
|
||||
mNsIndex(nsIndex),
|
||||
mReadOnly(readOnly),
|
||||
mPurgeAfterErase(purgeAfterErase),
|
||||
valid(1)
|
||||
{ }
|
||||
|
||||
@@ -56,6 +57,8 @@ public:
|
||||
|
||||
esp_err_t erase_all() override;
|
||||
|
||||
esp_err_t purge_all() override;
|
||||
|
||||
esp_err_t commit() override;
|
||||
|
||||
esp_err_t get_used_entry_count(size_t &usedEntries) override;
|
||||
@@ -91,11 +94,16 @@ private:
|
||||
|
||||
/**
|
||||
* Whether this handle is marked as read-only or read-write.
|
||||
* 0 indicates read-only, any other value read-write.
|
||||
* 1 indicates read-only, 0 indicates read-write.
|
||||
*/
|
||||
uint8_t mReadOnly;
|
||||
|
||||
/**
|
||||
* Whether this handle purges the erased or updated items right after erasing or updating them.
|
||||
* 0 indicates no purge, any other value indicates purge.
|
||||
*/
|
||||
|
||||
uint8_t mPurgeAfterErase; /**
|
||||
* Indicates the validity of this handle.
|
||||
* Upon opening, a handle is valid. It becomes invalid if the underlying storage is de-initialized.
|
||||
*/
|
||||
|
||||
@@ -276,7 +276,7 @@ esp_err_t Page::readVariableLengthItemData(const Item& item, const size_t index,
|
||||
dst += willCopy;
|
||||
}
|
||||
if (Item::calculateCrc32(reinterpret_cast<uint8_t * >(data), item.varLength.dataSize) != item.varLength.dataCrc32) {
|
||||
rc = eraseEntryAndSpan(index);
|
||||
rc = eraseEntryAndSpan(index, DEFAULT_PURGE_AFTER_ERASE);
|
||||
if (rc != ESP_OK) {
|
||||
return rc;
|
||||
}
|
||||
@@ -388,7 +388,7 @@ esp_err_t Page::cmpItem(uint8_t nsIndex, ItemType datatype, const char* key, con
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
esp_err_t Page::eraseItem(uint8_t nsIndex, ItemType datatype, const char* key, uint8_t chunkIdx, VerOffset chunkStart)
|
||||
esp_err_t Page::eraseItem(uint8_t nsIndex, ItemType datatype, const char* key, const bool purgeAfterErase, uint8_t chunkIdx, VerOffset chunkStart)
|
||||
{
|
||||
size_t index = 0;
|
||||
Item item;
|
||||
@@ -396,7 +396,60 @@ esp_err_t Page::eraseItem(uint8_t nsIndex, ItemType datatype, const char* key, u
|
||||
if (rc != ESP_OK) {
|
||||
return rc;
|
||||
}
|
||||
return eraseEntryAndSpan(index);
|
||||
return eraseEntryAndSpan(index, purgeAfterErase);
|
||||
}
|
||||
|
||||
esp_err_t Page::purgeErasedItems(uint8_t nsIndex)
|
||||
{
|
||||
if (mState == PageState::INVALID) {
|
||||
return ESP_ERR_NVS_INVALID_STATE;
|
||||
}
|
||||
|
||||
size_t index = 0;
|
||||
esp_err_t err;
|
||||
EntryState state;
|
||||
|
||||
while (index < ENTRY_COUNT) {
|
||||
err = mEntryTable.get(index, &state);
|
||||
if (err != ESP_OK) {
|
||||
return err;
|
||||
}
|
||||
if (state == EntryState::ERASED) {
|
||||
Item item;
|
||||
err = readEntry(index, item);
|
||||
if (err != ESP_OK) {
|
||||
return err;
|
||||
}
|
||||
|
||||
// check item sanity to avoid purging random data
|
||||
bool headerConsistent = item.checkHeaderConsistency(index);
|
||||
|
||||
// if header is not consistent, we cannot rely on nsIndex, so skip
|
||||
if (!headerConsistent) {
|
||||
++index;
|
||||
continue;
|
||||
}
|
||||
|
||||
// if nsIndex does not match, skip as well
|
||||
if (item.nsIndex != nsIndex) {
|
||||
++index;
|
||||
continue;
|
||||
}
|
||||
|
||||
// we have matching nsIndex and a consistent header, span is valid
|
||||
// purge entries belonging to the item's span range
|
||||
// we assume that if first entry is erased, the whole span is erased as well
|
||||
err = purgeEntryRange(index, index + item.span);
|
||||
if (err != ESP_OK) {
|
||||
return err;
|
||||
}
|
||||
index += item.span;
|
||||
continue;
|
||||
}
|
||||
// next entry
|
||||
++index;
|
||||
}
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
esp_err_t Page::findItem(uint8_t nsIndex, ItemType datatype, const char* key, uint8_t chunkIdx, VerOffset chunkStart)
|
||||
@@ -406,7 +459,7 @@ esp_err_t Page::findItem(uint8_t nsIndex, ItemType datatype, const char* key, ui
|
||||
return findItem(nsIndex, datatype, key, index, item, chunkIdx, chunkStart);
|
||||
}
|
||||
|
||||
esp_err_t Page::eraseEntryAndSpan(size_t index)
|
||||
esp_err_t Page::eraseEntryAndSpan(size_t index, const bool purgeAfterErase)
|
||||
{
|
||||
uint32_t seq_num;
|
||||
getSeqNumber(seq_num);
|
||||
@@ -432,6 +485,12 @@ esp_err_t Page::eraseEntryAndSpan(size_t index)
|
||||
if (rc != ESP_OK) {
|
||||
return rc;
|
||||
}
|
||||
if(purgeAfterErase) {
|
||||
rc = purgeEntry(index);
|
||||
if (rc != ESP_OK) {
|
||||
return rc;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
mHashList.erase(index);
|
||||
span = item.span;
|
||||
@@ -453,12 +512,28 @@ esp_err_t Page::eraseEntryAndSpan(size_t index)
|
||||
if (rc != ESP_OK) {
|
||||
return rc;
|
||||
}
|
||||
if(purgeAfterErase) {
|
||||
if(span == 1) {
|
||||
rc = purgeEntry(index);
|
||||
} else {
|
||||
rc = purgeEntryRange(index, index + span);
|
||||
}
|
||||
}
|
||||
if (rc != ESP_OK) {
|
||||
return rc;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
auto rc = alterEntryState(index, EntryState::ERASED);
|
||||
if (rc != ESP_OK) {
|
||||
return rc;
|
||||
}
|
||||
if(purgeAfterErase) {
|
||||
rc = purgeEntry(index);
|
||||
if (rc != ESP_OK) {
|
||||
return rc;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (index == mFirstUsedEntry) {
|
||||
@@ -635,6 +710,13 @@ esp_err_t Page::mLoadEntryTable()
|
||||
mState = PageState::INVALID;
|
||||
return err;
|
||||
}
|
||||
if(DEFAULT_PURGE_AFTER_ERASE) {
|
||||
err = purgeEntry(mNextFreeEntry);
|
||||
if (err != ESP_OK) {
|
||||
mState = PageState::INVALID;
|
||||
return err;
|
||||
}
|
||||
}
|
||||
++mNextFreeEntry;
|
||||
if (oldState == EntryState::WRITTEN) {
|
||||
--mUsedEntryCount;
|
||||
@@ -666,7 +748,7 @@ esp_err_t Page::mLoadEntryTable()
|
||||
|
||||
if (state == EntryState::ILLEGAL) {
|
||||
lastItemIndex = INVALID_ENTRY;
|
||||
auto err = eraseEntryAndSpan(i);
|
||||
auto err = eraseEntryAndSpan(i, DEFAULT_PURGE_AFTER_ERASE);
|
||||
if (err != ESP_OK) {
|
||||
mState = PageState::INVALID;
|
||||
return err;
|
||||
@@ -683,7 +765,7 @@ esp_err_t Page::mLoadEntryTable()
|
||||
}
|
||||
|
||||
if (!item.checkHeaderConsistency(i)) {
|
||||
err = eraseEntryAndSpan(i);
|
||||
err = eraseEntryAndSpan(i, DEFAULT_PURGE_AFTER_ERASE);
|
||||
if (err != ESP_OK) {
|
||||
mState = PageState::INVALID;
|
||||
return err;
|
||||
@@ -715,7 +797,7 @@ esp_err_t Page::mLoadEntryTable()
|
||||
}
|
||||
}
|
||||
if (needErase) {
|
||||
eraseEntryAndSpan(i);
|
||||
eraseEntryAndSpan(i, DEFAULT_PURGE_AFTER_ERASE);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
@@ -725,7 +807,7 @@ esp_err_t Page::mLoadEntryTable()
|
||||
* for same key on active page. Since datatype is not used in hash calculation,
|
||||
* old-format blob will be removed.*/
|
||||
if (duplicateIndex < i) {
|
||||
eraseEntryAndSpan(duplicateIndex);
|
||||
eraseEntryAndSpan(duplicateIndex, DEFAULT_PURGE_AFTER_ERASE);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -735,7 +817,7 @@ esp_err_t Page::mLoadEntryTable()
|
||||
Item dupItem;
|
||||
if (findItem(item.nsIndex, item.datatype, item.key, findItemIndex, dupItem) == ESP_OK) {
|
||||
if (findItemIndex < lastItemIndex) {
|
||||
auto err = eraseEntryAndSpan(findItemIndex);
|
||||
auto err = eraseEntryAndSpan(findItemIndex, DEFAULT_PURGE_AFTER_ERASE);
|
||||
if (err != ESP_OK) {
|
||||
mState = PageState::INVALID;
|
||||
return err;
|
||||
@@ -763,7 +845,7 @@ esp_err_t Page::mLoadEntryTable()
|
||||
}
|
||||
|
||||
if (!item.checkHeaderConsistency(i)) {
|
||||
err = eraseEntryAndSpan(i);
|
||||
err = eraseEntryAndSpan(i, DEFAULT_PURGE_AFTER_ERASE);
|
||||
if (err != ESP_OK) {
|
||||
mState = PageState::INVALID;
|
||||
return err;
|
||||
@@ -788,7 +870,7 @@ esp_err_t Page::mLoadEntryTable()
|
||||
return err;
|
||||
}
|
||||
if (state != EntryState::WRITTEN) {
|
||||
eraseEntryAndSpan(i);
|
||||
eraseEntryAndSpan(i, DEFAULT_PURGE_AFTER_ERASE);
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -897,6 +979,36 @@ esp_err_t Page::readEntry(size_t index, Item &dst) const
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
esp_err_t Page::purgeEntry(size_t index){
|
||||
return purgeEntryRange(index, index + 1);
|
||||
}
|
||||
|
||||
esp_err_t Page::purgeEntryRange(size_t begin, size_t end)
|
||||
{
|
||||
NVS_ASSERT_OR_RETURN(end <= ENTRY_COUNT, ESP_FAIL);
|
||||
NVS_ASSERT_OR_RETURN(end > begin, ESP_FAIL);
|
||||
esp_err_t err;
|
||||
uint32_t phyAddr;
|
||||
|
||||
uint8_t entryPurgeBuffer[ENTRY_SIZE];
|
||||
std::fill_n(entryPurgeBuffer, ENTRY_SIZE, 0x00);
|
||||
|
||||
for (size_t i = begin; i < end; i++) {
|
||||
err = getEntryAddress(i, &phyAddr);
|
||||
if (err != ESP_OK) {
|
||||
return err;
|
||||
}
|
||||
err = mPartition->write_raw(phyAddr, entryPurgeBuffer, ENTRY_SIZE);
|
||||
if (err != ESP_OK) {
|
||||
mState = PageState::INVALID;
|
||||
return err;
|
||||
}
|
||||
}
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
|
||||
|
||||
esp_err_t Page::findItem(uint8_t nsIndex, ItemType datatype, const char* key, size_t &itemIndex, Item &item, uint8_t chunkIdx, VerOffset chunkStart)
|
||||
{
|
||||
if (mState == PageState::CORRUPT || mState == PageState::INVALID || mState == PageState::UNINITIALIZED) {
|
||||
@@ -950,7 +1062,7 @@ esp_err_t Page::findItem(uint8_t nsIndex, ItemType datatype, const char* key, si
|
||||
}
|
||||
|
||||
if (!item.checkHeaderConsistency(i)) {
|
||||
rc = eraseEntryAndSpan(i);
|
||||
rc = eraseEntryAndSpan(i, DEFAULT_PURGE_AFTER_ERASE);
|
||||
if (rc != ESP_OK) {
|
||||
mState = PageState::INVALID;
|
||||
return rc;
|
||||
|
||||
@@ -18,6 +18,8 @@ namespace nvs
|
||||
class Page : public intrusive_list_node<Page>, public ExceptionlessAllocatable
|
||||
{
|
||||
public:
|
||||
static const bool DEFAULT_PURGE_AFTER_ERASE = true;
|
||||
|
||||
static const uint32_t PSB_INIT = NVS_CONST_PSB_INIT;
|
||||
static const uint32_t PSB_FULL = NVS_CONST_PSB_FULL;
|
||||
static const uint32_t PSB_FREEING = NVS_CONST_PSB_FREEING;
|
||||
@@ -86,13 +88,15 @@ public:
|
||||
|
||||
esp_err_t cmpItem(uint8_t nsIndex, ItemType datatype, const char* key, const void* data, size_t dataSize, uint8_t chunkIdx = CHUNK_ANY, VerOffset chunkStart = VerOffset::VER_ANY);
|
||||
|
||||
esp_err_t eraseItem(uint8_t nsIndex, ItemType datatype, const char* key, uint8_t chunkIdx = CHUNK_ANY, VerOffset chunkStart = VerOffset::VER_ANY);
|
||||
esp_err_t eraseItem(uint8_t nsIndex, ItemType datatype, const char* key, const bool purgeAfterErase, uint8_t chunkIdx = CHUNK_ANY, VerOffset chunkStart = VerOffset::VER_ANY);
|
||||
|
||||
esp_err_t purgeErasedItems(uint8_t nsIndex);
|
||||
|
||||
esp_err_t findItem(uint8_t nsIndex, ItemType datatype, const char* key, uint8_t chunkIdx = CHUNK_ANY, VerOffset chunkStart = VerOffset::VER_ANY);
|
||||
|
||||
esp_err_t findItem(uint8_t nsIndex, ItemType datatype, const char* key, size_t &itemIndex, Item& item, uint8_t chunkIdx = CHUNK_ANY, VerOffset chunkStart = VerOffset::VER_ANY);
|
||||
|
||||
esp_err_t eraseEntryAndSpan(size_t index);
|
||||
esp_err_t eraseEntryAndSpan(size_t index, const bool purgeAfterErase);
|
||||
|
||||
template<typename T>
|
||||
esp_err_t writeItem(uint8_t nsIndex, const char* key, const T& value)
|
||||
@@ -113,9 +117,9 @@ public:
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
esp_err_t eraseItem(uint8_t nsIndex, const char* key)
|
||||
esp_err_t eraseItem(uint8_t nsIndex, const char* key, const bool purgeAfterErase)
|
||||
{
|
||||
return eraseItem(nsIndex, itemTypeOf<T>(), key);
|
||||
return eraseItem(nsIndex, itemTypeOf<T>(), key, purgeAfterErase);
|
||||
}
|
||||
|
||||
size_t getUsedEntryCount() const
|
||||
@@ -178,6 +182,10 @@ protected:
|
||||
|
||||
esp_err_t alterPageState(PageState state);
|
||||
|
||||
esp_err_t purgeEntryRange(size_t begin, size_t end);
|
||||
|
||||
esp_err_t purgeEntry(size_t index);
|
||||
|
||||
esp_err_t readEntry(size_t index, Item& dst) const;
|
||||
|
||||
esp_err_t writeEntry(const Item& item);
|
||||
|
||||
@@ -75,7 +75,7 @@ esp_err_t PageManager::load(Partition *partition, uint32_t baseSector, uint32_t
|
||||
for (it = begin(); it != last; ++it) {
|
||||
|
||||
if ((it->state() != Page::PageState::FREEING) &&
|
||||
(it->eraseItem(item.nsIndex, item.datatype, item.key, item.chunkIndex) == ESP_OK)) {
|
||||
(it->eraseItem(item.nsIndex, item.datatype, item.key, Page::DEFAULT_PURGE_AFTER_ERASE, item.chunkIndex) == ESP_OK)) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -85,7 +85,7 @@ esp_err_t PageManager::load(Partition *partition, uint32_t baseSector, uint32_t
|
||||
for (it = begin(); it != last; ++it) {
|
||||
|
||||
if ((it->state() != Page::PageState::FREEING) &&
|
||||
(it->eraseItem(item.nsIndex, ItemType::BLOB, item.key, item.chunkIndex) == ESP_OK)) {
|
||||
(it->eraseItem(item.nsIndex, ItemType::BLOB, item.key, Page::DEFAULT_PURGE_AFTER_ERASE, item.chunkIndex) == ESP_OK)) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -195,17 +195,19 @@ esp_err_t NVSPartitionManager::open_handle(const char *part_name,
|
||||
return ESP_ERR_NVS_PART_NOT_FOUND;
|
||||
}
|
||||
|
||||
if (open_mode == NVS_READWRITE && const_cast<Partition*>(sHandle->getPart())->get_readonly()) {
|
||||
#define WANTS_WRITE_MODE(mode) ((mode) == NVS_READWRITE || (mode) == NVS_READWRITE_PURGE)
|
||||
|
||||
if (WANTS_WRITE_MODE(open_mode) && const_cast<Partition*>(sHandle->getPart())->get_readonly()) {
|
||||
return ESP_ERR_NOT_ALLOWED;
|
||||
}
|
||||
|
||||
|
||||
esp_err_t err = sHandle->createOrOpenNamespace(ns_name, open_mode == NVS_READWRITE, nsIndex);
|
||||
esp_err_t err = sHandle->createOrOpenNamespace(ns_name, WANTS_WRITE_MODE(open_mode), nsIndex);
|
||||
if (err != ESP_OK) {
|
||||
return err;
|
||||
}
|
||||
|
||||
NVSHandleSimple* new_handle = new (std::nothrow) NVSHandleSimple(open_mode==NVS_READONLY, nsIndex, sHandle);
|
||||
NVSHandleSimple* new_handle = new (std::nothrow) NVSHandleSimple(open_mode==NVS_READONLY, nsIndex, sHandle, open_mode==NVS_READWRITE_PURGE);
|
||||
if (new_handle == nullptr) {
|
||||
return ESP_ERR_NO_MEM;
|
||||
}
|
||||
|
||||
@@ -132,7 +132,7 @@ void Storage::eraseMismatchedBlobIndexes(TBlobIndexList& blobIdxList)
|
||||
}
|
||||
|
||||
Page& p = *it;
|
||||
if(p.eraseItem(iter->nsIndex, nvs::ItemType::BLOB_IDX, iter->key, 255, iter->chunkStart) == ESP_OK){
|
||||
if(p.eraseItem(iter->nsIndex, nvs::ItemType::BLOB_IDX, iter->key, Page::DEFAULT_PURGE_AFTER_ERASE, 255, iter->chunkStart) == ESP_OK){
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -172,7 +172,7 @@ void Storage::eraseOrphanDataBlobs(TBlobIndexList& blobIdxList)
|
||||
&& (item.chunkIndex >= static_cast<uint8_t> (e.chunkStart))
|
||||
&& (item.chunkIndex < static_cast<uint8_t> (e.chunkStart) + e.chunkCount);});
|
||||
if(iter == std::end(blobIdxList)) {
|
||||
p.eraseItem(item.nsIndex, item.datatype, item.key, item.chunkIndex);
|
||||
p.eraseItem(item.nsIndex, item.datatype, item.key, Page::DEFAULT_PURGE_AFTER_ERASE, item.chunkIndex);
|
||||
}
|
||||
|
||||
itemIndex += item.span;
|
||||
@@ -270,7 +270,7 @@ esp_err_t Storage::findItem(uint8_t nsIndex, ItemType datatype, const char* key,
|
||||
return ESP_ERR_NVS_NOT_FOUND;
|
||||
}
|
||||
|
||||
esp_err_t Storage::writeMultiPageBlob(uint8_t nsIndex, const char* key, const void* data, size_t dataSize, VerOffset chunkStart)
|
||||
esp_err_t Storage::writeMultiPageBlob(uint8_t nsIndex, const char* key, const void* data, size_t dataSize, VerOffset chunkStart, const bool purgeAfterErase)
|
||||
{
|
||||
uint8_t chunkCount = 0;
|
||||
TUsedPageList usedPages;
|
||||
@@ -368,7 +368,7 @@ esp_err_t Storage::writeMultiPageBlob(uint8_t nsIndex, const char* key, const vo
|
||||
/* Anything failed, then we should erase all the written chunks*/
|
||||
int ii=0;
|
||||
for(auto it = std::begin(usedPages); it != std::end(usedPages); it++) {
|
||||
it->mPage->eraseItem(nsIndex, ItemType::BLOB_DATA, key, ii++);
|
||||
it->mPage->eraseItem(nsIndex, ItemType::BLOB_DATA, key, purgeAfterErase, ii++);
|
||||
}
|
||||
}
|
||||
usedPages.clearAndFreeNodes();
|
||||
@@ -377,7 +377,7 @@ esp_err_t Storage::writeMultiPageBlob(uint8_t nsIndex, const char* key, const vo
|
||||
|
||||
// datatype BLOB is written as BLOB_INDEX and BLOB_DATA and is searched for previous value as BLOB_INDEX and/or BLOB
|
||||
// datatype BLOB_INDEX and BLOB_DATA are not supported as input parameters, the layer above should always use BLOB
|
||||
esp_err_t Storage::writeItem(uint8_t nsIndex, ItemType datatype, const char* key, const void* data, size_t dataSize)
|
||||
esp_err_t Storage::writeItem(uint8_t nsIndex, ItemType datatype, const char* key, const void* data, size_t dataSize, const bool purgeAfterErase)
|
||||
{
|
||||
if(mState != StorageState::ACTIVE) {
|
||||
return ESP_ERR_NVS_NOT_INITIALIZED;
|
||||
@@ -488,7 +488,7 @@ esp_err_t Storage::writeItem(uint8_t nsIndex, ItemType datatype, const char* key
|
||||
= (prevStart == VerOffset::VER_1_OFFSET) ? VerOffset::VER_0_OFFSET : VerOffset::VER_1_OFFSET;
|
||||
}
|
||||
// Write the blob with new version
|
||||
err = writeMultiPageBlob(nsIndex, key, data, dataSize, nextStart);
|
||||
err = writeMultiPageBlob(nsIndex, key, data, dataSize, nextStart, purgeAfterErase);
|
||||
|
||||
if(err == ESP_ERR_NVS_PAGE_FULL) {
|
||||
return ESP_ERR_NVS_NOT_ENOUGH_SPACE;
|
||||
@@ -543,7 +543,7 @@ esp_err_t Storage::writeItem(uint8_t nsIndex, ItemType datatype, const char* key
|
||||
// If the item found was BLOB_INDEX, the eraseMultiPageBlob is used to erase the whole multi-page blob.
|
||||
// It is not necessary to check the potential page relocation as the function will find the blob again anyway.
|
||||
VerOffset prevStart = item.blobIndex.chunkStart;
|
||||
err = eraseMultiPageBlob(nsIndex, key, prevStart);
|
||||
err = eraseMultiPageBlob(nsIndex, key, purgeAfterErase, prevStart);
|
||||
|
||||
if(err == ESP_ERR_FLASH_OP_FAIL) {
|
||||
return ESP_ERR_NVS_REMOVE_FAILED;
|
||||
@@ -583,7 +583,7 @@ esp_err_t Storage::writeItem(uint8_t nsIndex, ItemType datatype, const char* key
|
||||
}
|
||||
|
||||
// Page containing the old value is now refreshed. We can erase the old value.
|
||||
err = findPage->eraseEntryAndSpan(itemIndex);
|
||||
err = findPage->eraseEntryAndSpan(itemIndex, purgeAfterErase);
|
||||
if(err == ESP_ERR_FLASH_OP_FAIL) {
|
||||
return ESP_ERR_NVS_REMOVE_FAILED;
|
||||
}
|
||||
@@ -623,7 +623,7 @@ esp_err_t Storage::createOrOpenNamespace(const char* nsName, bool canCreate, uin
|
||||
return ESP_ERR_NVS_NOT_ENOUGH_SPACE;
|
||||
}
|
||||
|
||||
auto err = writeItem(Page::NS_INDEX, ItemType::U8, nsName, &ns, sizeof(ns));
|
||||
auto err = writeItem(Page::NS_INDEX, ItemType::U8, nsName, &ns, sizeof(ns), Page::DEFAULT_PURGE_AFTER_ERASE);
|
||||
if(err != ESP_OK) {
|
||||
return err;
|
||||
}
|
||||
@@ -696,7 +696,7 @@ esp_err_t Storage::readMultiPageBlob(uint8_t nsIndex, const char* key, void* dat
|
||||
|
||||
if(err == ESP_ERR_NVS_NOT_FOUND || err == ESP_ERR_NVS_INVALID_LENGTH) {
|
||||
// cleanup if a chunk is not found or the size is inconsistent
|
||||
eraseMultiPageBlob(nsIndex, key);
|
||||
eraseMultiPageBlob(nsIndex, key, Page::DEFAULT_PURGE_AFTER_ERASE);
|
||||
}
|
||||
|
||||
NVS_ASSERT_OR_RETURN(offset == dataSize, ESP_FAIL);
|
||||
@@ -783,7 +783,7 @@ esp_err_t Storage::readItem(uint8_t nsIndex, ItemType datatype, const char* key,
|
||||
|
||||
}
|
||||
|
||||
esp_err_t Storage::eraseMultiPageBlob(uint8_t nsIndex, const char* key, VerOffset chunkStart)
|
||||
esp_err_t Storage::eraseMultiPageBlob(uint8_t nsIndex, const char* key, const bool purgeAfterErase, VerOffset chunkStart)
|
||||
{
|
||||
if(mState != StorageState::ACTIVE) {
|
||||
return ESP_ERR_NVS_NOT_INITIALIZED;
|
||||
@@ -801,7 +801,7 @@ esp_err_t Storage::eraseMultiPageBlob(uint8_t nsIndex, const char* key, VerOffse
|
||||
chunkCount = item.blobIndex.chunkCount;
|
||||
|
||||
// Erase the index first and make children blobs orphan
|
||||
err = findPage->eraseEntryAndSpan(itemIndex);
|
||||
err = findPage->eraseEntryAndSpan(itemIndex, purgeAfterErase);
|
||||
if(err != ESP_OK) {
|
||||
return err;
|
||||
}
|
||||
@@ -817,7 +817,7 @@ esp_err_t Storage::eraseMultiPageBlob(uint8_t nsIndex, const char* key, VerOffse
|
||||
// Ignore potential error if the item is not found
|
||||
err = findItem(nsIndex, ItemType::BLOB_IDX, key, findPage, item, Page::CHUNK_ANY, chunkStart, &itemIndex);
|
||||
if(err == ESP_OK) {
|
||||
err = findPage->eraseEntryAndSpan(itemIndex);
|
||||
err = findPage->eraseEntryAndSpan(itemIndex, purgeAfterErase);
|
||||
if(err != ESP_OK) {
|
||||
return err;
|
||||
}
|
||||
@@ -838,7 +838,7 @@ esp_err_t Storage::eraseMultiPageBlob(uint8_t nsIndex, const char* key, VerOffse
|
||||
if(err == ESP_ERR_NVS_NOT_FOUND) {
|
||||
break;
|
||||
} else if(err == ESP_OK) {
|
||||
err = it->eraseEntryAndSpan(itemIndex);
|
||||
err = it->eraseEntryAndSpan(itemIndex, purgeAfterErase);
|
||||
|
||||
// advance itemIndex to the next potential entry on the page
|
||||
// findItem checks the consistency of the entry metadata so we can safely assume the span is non-zero
|
||||
@@ -874,7 +874,7 @@ esp_err_t Storage::eraseMultiPageBlob(uint8_t nsIndex, const char* key, VerOffse
|
||||
}
|
||||
|
||||
// Erase the entry
|
||||
err = findPage->eraseEntryAndSpan(itemIndex);
|
||||
err = findPage->eraseEntryAndSpan(itemIndex, purgeAfterErase);
|
||||
if(err != ESP_OK) {
|
||||
return err;
|
||||
}
|
||||
@@ -884,7 +884,7 @@ esp_err_t Storage::eraseMultiPageBlob(uint8_t nsIndex, const char* key, VerOffse
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
esp_err_t Storage::eraseItem(uint8_t nsIndex, ItemType datatype, const char* key)
|
||||
esp_err_t Storage::eraseItem(uint8_t nsIndex, ItemType datatype, const char* key, const bool purgeAfterErase)
|
||||
{
|
||||
if(mState != StorageState::ACTIVE) {
|
||||
return ESP_ERR_NVS_NOT_INITIALIZED;
|
||||
@@ -901,13 +901,13 @@ esp_err_t Storage::eraseItem(uint8_t nsIndex, ItemType datatype, const char* key
|
||||
}
|
||||
// If the item found is BLOB_IDX, the eraseMultiPageBlob is used to erase the whole multi-page blob.
|
||||
if (item.datatype == ItemType::BLOB_IDX) {
|
||||
return eraseMultiPageBlob(nsIndex, key, item.blobIndex.chunkStart);
|
||||
return eraseMultiPageBlob(nsIndex, key, purgeAfterErase, item.blobIndex.chunkStart);
|
||||
}
|
||||
|
||||
return findPage->eraseEntryAndSpan(itemIndex);
|
||||
return findPage->eraseEntryAndSpan(itemIndex, purgeAfterErase);
|
||||
}
|
||||
|
||||
esp_err_t Storage::eraseNamespace(uint8_t nsIndex)
|
||||
esp_err_t Storage::eraseNamespace(uint8_t nsIndex, const bool purgeAfterErase)
|
||||
{
|
||||
if(mState != StorageState::ACTIVE) {
|
||||
return ESP_ERR_NVS_NOT_INITIALIZED;
|
||||
@@ -915,7 +915,7 @@ esp_err_t Storage::eraseNamespace(uint8_t nsIndex)
|
||||
|
||||
for(auto it = std::begin(mPageManager); it != std::end(mPageManager); ++it) {
|
||||
while(true) {
|
||||
auto err = it->eraseItem(nsIndex, ItemType::ANY, nullptr);
|
||||
auto err = it->eraseItem(nsIndex, ItemType::ANY, nullptr, purgeAfterErase);
|
||||
if(err == ESP_ERR_NVS_NOT_FOUND) {
|
||||
break;
|
||||
}
|
||||
@@ -925,7 +925,20 @@ esp_err_t Storage::eraseNamespace(uint8_t nsIndex)
|
||||
}
|
||||
}
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
esp_err_t Storage::purgeNamespace(uint8_t nsIndex){
|
||||
if(mState != StorageState::ACTIVE) {
|
||||
return ESP_ERR_NVS_NOT_INITIALIZED;
|
||||
}
|
||||
|
||||
for(auto it = std::begin(mPageManager); it != std::end(mPageManager); ++it) {
|
||||
auto err = it->purgeErasedItems(nsIndex);
|
||||
if(err != ESP_OK) {
|
||||
return err;
|
||||
}
|
||||
}
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
esp_err_t Storage::findKey(const uint8_t nsIndex, const char* key, ItemType* datatype)
|
||||
|
||||
@@ -69,7 +69,7 @@ public:
|
||||
|
||||
esp_err_t createOrOpenNamespace(const char* nsName, bool canCreate, uint8_t& nsIndex);
|
||||
|
||||
esp_err_t writeItem(uint8_t nsIndex, ItemType datatype, const char* key, const void* data, size_t dataSize);
|
||||
esp_err_t writeItem(uint8_t nsIndex, ItemType datatype, const char* key, const void* data, size_t dataSize, const bool purgeAfterErase);
|
||||
|
||||
esp_err_t readItem(uint8_t nsIndex, ItemType datatype, const char* key, void* data, size_t dataSize);
|
||||
|
||||
@@ -77,12 +77,12 @@ public:
|
||||
|
||||
esp_err_t getItemDataSize(uint8_t nsIndex, ItemType datatype, const char* key, size_t& dataSize);
|
||||
|
||||
esp_err_t eraseItem(uint8_t nsIndex, ItemType datatype, const char* key);
|
||||
esp_err_t eraseItem(uint8_t nsIndex, ItemType datatype, const char* key, const bool purgeAfterErase);
|
||||
|
||||
template<typename T>
|
||||
esp_err_t writeItem(uint8_t nsIndex, const char* key, const T& value)
|
||||
esp_err_t writeItem(uint8_t nsIndex, const char* key, const T& value, const bool purgeAfterErase)
|
||||
{
|
||||
return writeItem(nsIndex, itemTypeOf(value), key, &value, sizeof(value));
|
||||
return writeItem(nsIndex, itemTypeOf(value), key, &value, sizeof(value), purgeAfterErase);
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
@@ -91,12 +91,14 @@ public:
|
||||
return readItem(nsIndex, itemTypeOf(value), key, &value, sizeof(value));
|
||||
}
|
||||
|
||||
esp_err_t eraseItem(uint8_t nsIndex, const char* key)
|
||||
esp_err_t eraseItem(uint8_t nsIndex, const char* key, const bool purgeAfterErase)
|
||||
{
|
||||
return eraseItem(nsIndex, ItemType::ANY, key);
|
||||
return eraseItem(nsIndex, ItemType::ANY, key, purgeAfterErase);
|
||||
}
|
||||
|
||||
esp_err_t eraseNamespace(uint8_t nsIndex);
|
||||
esp_err_t eraseNamespace(uint8_t nsIndex, const bool purgeAfterErase);
|
||||
|
||||
esp_err_t purgeNamespace(uint8_t nsIndex);
|
||||
|
||||
const Partition *getPart() const
|
||||
{
|
||||
@@ -113,13 +115,13 @@ public:
|
||||
return mPageManager.getBaseSector();
|
||||
}
|
||||
|
||||
esp_err_t writeMultiPageBlob(uint8_t nsIndex, const char* key, const void* data, size_t dataSize, VerOffset chunkStart);
|
||||
esp_err_t writeMultiPageBlob(uint8_t nsIndex, const char* key, const void* data, size_t dataSize, VerOffset chunkStart, const bool purgeAfterErase);
|
||||
|
||||
esp_err_t readMultiPageBlob(uint8_t nsIndex, const char* key, void* data, size_t dataSize);
|
||||
|
||||
esp_err_t cmpMultiPageBlob(uint8_t nsIndex, const char* key, const void* data, size_t dataSize);
|
||||
|
||||
esp_err_t eraseMultiPageBlob(uint8_t nsIndex, const char* key, VerOffset chunkStart = VerOffset::VER_ANY);
|
||||
esp_err_t eraseMultiPageBlob(uint8_t nsIndex, const char* key, const bool purgeAfterErase, VerOffset chunkStart = VerOffset::VER_ANY);
|
||||
|
||||
void debugDump();
|
||||
|
||||
|
||||
Reference in New Issue
Block a user