#pragma once #include #include #include #include #include "NodePrefs.h" #if MESH_CONTACT_CACHE #include #endif #include #if defined(ESP32_PLATFORM) namespace mesh { class ContactFileTransaction; } #endif #if COMPANION_FEATURE_READER #include #endif class DataStoreHost { public: virtual bool onContactLoaded(const ContactInfo& contact) =0; virtual bool getContactForSave(uint32_t idx, ContactInfo& contact) =0; virtual ContactInfo* getContactForStore(uint32_t idx) =0; virtual void onContactCacheFlushed() {} virtual bool onChannelLoaded(uint8_t channel_idx, const ChannelDetails& ch) =0; virtual bool getChannelForSave(uint8_t channel_idx, ChannelDetails& ch) =0; }; class DataStore #if MESH_CONTACT_CACHE : public mesh::ContactPathBackend #if MESH_CONTACT_SECRET_FLASH_CACHE , public mesh::ContactSecretBackend #endif #endif { FILESYSTEM* _fs; FILESYSTEM* _fsExtra; // Keep the configured secondary even when normal I/O falls back to the // primary filesystem. Boot recovery and explicit local repair must still // be able to address the inactive on-chip ExtraFS. FILESYSTEM* _configuredFsExtra; mesh::RTCClock* _clock; IdentityStore identity_store; bool _identity_creation_blocked = false; bool _prefs_load_incomplete = false; #if defined(ESP32_PLATFORM) const char* _prefs_recovery_source = nullptr; const char* _channel_recovery_source = nullptr; mesh::ContactFileTransaction* _contact_write = nullptr; DataStoreHost* _contact_write_host = nullptr; bool (*_contact_write_filter)(const ContactInfo&) = nullptr; uint32_t _contact_write_revision = 0; uint32_t _contact_write_active_revision = 0; uint32_t _contact_write_index = 0; bool _contact_write_verifying = false; bool _contact_write_requested = false; bool _contact_write_servicing = false; void cancelContactWrite(); bool serviceContactWrite(DataStoreHost* host, bool (*filter)(const ContactInfo&)); #endif #if defined(ESP32_PLATFORM) // Advert packets are reconstructable cache data. Keep accepted updates in // RAM while their filesystem operations run on separate loop passes. struct AdvertWriteState { static constexpr uint8_t CAPACITY = 4; struct Slot { uint8_t key[8] = {}; uint8_t bytes[255] = {}; uint32_t revision = 0; uint8_t key_len = 0; uint8_t len = 0; } slots[CAPACITY]; enum class Stage : uint8_t { Idle, TargetProbe, BackupProbe, RestoreBackup, RemoveBackup, TempProbe, RemoveTemp, OpenWrite, ConfigureWrite, Write, CloseWrite, OpenVerify, ConfigureVerify, VerifySize, Verify, CloseVerify, PublishTargetProbe, PublishBackupProbe, PublishRemoveBackup, PublishBackup, Publish, PublishCleanup, CancelClose } stage = Stage::Idle; File file; uint8_t scratch[64] = {}; uint8_t active_key[8] = {}; uint8_t active_key_len = 0; char target[24] = {}; char backup[28] = {}; uint32_t revision = 0; uint32_t active_revision = 0; uint32_t retry_at = 0; uint8_t active = CAPACITY; uint8_t next = 0; uint8_t offset = 0; uint8_t failures = 0; bool target_present = false; bool backup_present = false; bool temp_present = false; bool cancelling = false; bool synchronous_io = false; ~AdvertWriteState() { file.close(); } void pause() { file.close(); stage = Stage::Idle; active = CAPACITY; retry_at = 0; } void clear() { pause(); for (auto& slot : slots) slot.len = 0; failures = 0; } } _advert_write; void invalidateAdvertWrite(const uint8_t key[], int key_len); void retireAdvertWrite(const uint8_t key[], int key_len); #endif #if !defined(NRF52_PLATFORM) bool _channel_load_incomplete = false; #if !MESH_CONTACT_CACHE bool _uncached_contact_load_incomplete = false; #endif #endif #if MESH_CONTACT_CACHE DataStoreHost* _cache_host = nullptr; bool _cache_load_incomplete = false; #if defined(ESP32_PLATFORM) File _contact_path_reader; #endif bool readStoredPath(uint16_t source, uint8_t path[64]) override; bool flushCachedPaths() override; #if defined(ESP32_PLATFORM) bool cancelCooperativeWrite() override; #endif #if MESH_CONTACT_SECRET_FLASH_CACHE uint32_t _secret_retry_at = 0; bool readSavedSecret(const uint8_t peer[32], const uint8_t identity[32], uint8_t secret[32]) override; bool saveSecret(const uint8_t peer[32], const uint8_t identity[32], const uint8_t secret[32]) override; uint16_t secretSlot(const uint8_t peer[32]) const; #endif #endif #if defined(NRF52_PLATFORM) mesh::storage::ContactSlotMap _contact_slots; mesh::storage::DirtyPageSet _dirty_contact_pages; mesh::storage::DirtyPageSet _unread_contact_pages; bool _contact_load_incomplete = false; bool _primary_storage_unavailable = false; bool _secondary_authority_unknown = false; bool _volatile_primary_fs = false; uint32_t _contact_page_generations[mesh::storage::CONTACT_PAGE_COUNT]; bool _legacy_contacts_pending_cleanup; bool _legacy_migration_ready; uint16_t _legacy_contact_count; bool prepareLegacyContactMigration(); bool loadContactPages(DataStoreHost* host, uint16_t minimum_slot, uint32_t expected_page_mask); bool writeContactPage(DataStoreHost* host, uint8_t page, bool (*filter)(const ContactInfo& c)); bool truncateLegacyContacts(uint16_t remaining_contacts); void resetContactPageState(bool clear_incomplete = false); #if defined(EXTRAFS) && !defined(QSPIFLASH) bool recoverInternalExtraFSOnBoot(); bool reinitializeInternalExtraFS(bool scan_physical_pages = false); #endif #endif bool loadPrefsInt(const char *filename, CompanionNodePrefs& prefs, double& node_lat, double& node_lon); #if defined(NRF52_PLATFORM) || defined(STM32_PLATFORM) void checkAdvBlobFile(); #endif public: DataStore(FILESYSTEM& fs, mesh::RTCClock& clock); ~DataStore(); DataStore(FILESYSTEM& fs, FILESYSTEM& fsExtra, mesh::RTCClock& clock); void begin(); bool formatFileSystem(); bool repairInternalExtraFS(); #if defined(NRF52_PLATFORM) && defined(EXTRAFS) && !defined(QSPIFLASH) bool requestInternalExtraFSBootScan(); bool formatInternalExtraFSHealth(char* reply, size_t reply_size) const; #endif FILESYSTEM* getPrimaryFS() const { return _fs; } FILESYSTEM* getSecondaryFS() const { return _fsExtra; } #if defined(NRF52_PLATFORM) void useVolatilePrimaryFS(FILESYSTEM& fs); bool isVolatilePrimaryFS() const { return _volatile_primary_fs; } // Meshtext settings belong to the primary store, alongside the identity. // An absent record is size zero; unreadable or volatile storage is failure. bool loadMeshtextSettings(uint8_t image[40], size_t& size); bool saveMeshtextSettings(const uint8_t image[40]); bool eraseMeshtextSettings(); #endif void markPrimaryFSUnavailable(); void disableSecondaryFS(bool authority_unknown = true); bool loadMainIdentity(mesh::LocalIdentity &identity); bool canCreateMainIdentity() const; bool saveMainIdentity(const mesh::LocalIdentity &identity); bool loadPrefs(CompanionNodePrefs& prefs, double& node_lat, double& node_lon); bool savePrefs(const CompanionNodePrefs& prefs, double node_lat, double node_lon); #if COMPANION_FEATURE_READER bool loadReaderBookmark(mesh::bible::Position& pos); bool saveReaderBookmark(mesh::bible::Position pos); #endif void loadContacts(DataStoreHost* host); bool saveContacts(DataStoreHost* host, bool (*filter)(const ContactInfo& c) = NULL); bool markContactDirty(const ContactInfo& contact); bool releaseContact(const ContactInfo& contact); bool restoreContactSlot(const ContactInfo& contact, uint16_t slot); bool serviceContactWrites(DataStoreHost* host, bool (*filter)(const ContactInfo& c) = NULL); bool flushContactWrites(DataStoreHost* host, bool (*filter)(const ContactInfo& c) = NULL); bool hasPendingContactWrites() const; bool hasIncompleteContactLoad() const; void loadChannels(DataStoreHost* host); bool saveChannels(DataStoreHost* host); bool migrateToSecondaryFS(); uint8_t getBlobByKey(const uint8_t key[], int key_len, uint8_t dest_buf[]); bool putBlobByKey(const uint8_t key[], int key_len, const uint8_t src_buf[], uint8_t len); bool deleteBlobByKey(const uint8_t key[], int key_len); #if defined(ESP32_PLATFORM) bool queueAdvertByKey(const uint8_t key[], int key_len, const uint8_t src_buf[], uint8_t len); bool serviceAdvertWrites(uint32_t now); bool flushAdvertWrites(uint32_t now = 0); bool hasPendingAdvertWrites() const; bool isAdvertWriteDue(uint32_t now) const; bool consumeSynchronousAdvertIO(); #endif File openRead(const char* filename); File openRead(FILESYSTEM* fs, const char* filename); File openDirectory(const char* path); File openDirectory(FILESYSTEM* fs, const char* path); bool removeFile(const char* filename); bool removeFile(FILESYSTEM* fs, const char* filename); uint32_t getStorageUsedKb() const; uint32_t getStorageTotalKb() const; private: FILESYSTEM* _getContactsChannelsFS() const { if (_fsExtra) return _fsExtra; return _fs;}; };