Line data Source code
1 : /**
2 : *
3 : * Copyright (c) 2025 Project CHIP Authors
4 : *
5 : * Licensed under the Apache License, Version 2.0 (the "License");
6 : * you may not use this file except in compliance with the License.
7 : * You may obtain a copy of the License at
8 : *
9 : * http://www.apache.org/licenses/LICENSE-2.0
10 : *
11 : * Unless required by applicable law or agreed to in writing, software
12 : * distributed under the License is distributed on an "AS IS" BASIS,
13 : * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 : * See the License for the specific language governing permissions and
15 : * limitations under the License.
16 : */
17 :
18 : #pragma once
19 :
20 : #include <app/data-model-provider/MetadataTypes.h>
21 : #include <app/storage/FabricTableImpl.h>
22 : #include <app/util/endpoint-config-api.h>
23 : #include <lib/support/CodeUtils.h>
24 : #include <lib/support/DefaultStorageKeyAllocator.h>
25 : #include <lib/support/ReadOnlyBuffer.h>
26 : #include <lib/support/TypeTraits.h>
27 :
28 : #include <cstdlib>
29 :
30 : namespace chip {
31 : namespace app {
32 : namespace Storage {
33 :
34 : using EntryIndex = Data::EntryIndex;
35 :
36 : /// @brief Tags Used to serialize entries so they can be stored in flash memory.
37 : /// kEndpointEntryCount: Number of entries in an endpoint
38 : /// kEntryCount: Number of entries in a Fabric
39 : /// kStorageIdArray: Array of StorageId struct
40 : enum class TagEntry : uint8_t
41 : {
42 : kEndpointEntryCount = 1,
43 : kEntryCount,
44 : kStorageIdArray,
45 : kFabricTableFirstSpecializationReservedTag,
46 : kFabricTableLastSpecializationReservedTag = 127,
47 : // Add new entries here; kFabricTableFirstSpecializationReservedTag through
48 : // kFabricTableLastSpecializationReservedTag are reserved for specializations
49 : };
50 :
51 : // Currently takes 5 Bytes to serialize Container and value in a TLV: 1 byte start struct, 2 bytes control + tag for the value, 1
52 : // byte value, 1 byte end struct. 8 Bytes leaves space for potential increase in count_value size.
53 : static constexpr size_t kPersistentBufferEntryCountBytes = 8;
54 :
55 : struct BaseEntryCount : public PersistableData<kPersistentBufferEntryCountBytes>
56 : {
57 : uint8_t count_value = 0;
58 607 : BaseEntryCount(uint8_t count = 0) : count_value(count) {}
59 :
60 607 : void Clear() override { count_value = 0; }
61 :
62 290 : CHIP_ERROR Serialize(TLV::TLVWriter & writer) const override
63 : {
64 : TLV::TLVType container;
65 290 : ReturnErrorOnFailure(writer.StartContainer(TLV::AnonymousTag(), TLV::kTLVType_Structure, container));
66 290 : ReturnErrorOnFailure(writer.Put(TLV::ContextTag(TagEntry::kEndpointEntryCount), count_value));
67 290 : return writer.EndContainer(container);
68 : }
69 :
70 542 : CHIP_ERROR Deserialize(TLV::TLVReader & reader) override
71 : {
72 542 : ReturnErrorOnFailure(reader.Next(TLV::kTLVType_Structure, TLV::AnonymousTag()));
73 :
74 : TLV::TLVType container;
75 542 : ReturnErrorOnFailure(reader.EnterContainer(container));
76 542 : ReturnErrorOnFailure(reader.Next(TLV::ContextTag(TagEntry::kEndpointEntryCount)));
77 542 : ReturnErrorOnFailure(reader.Get(count_value));
78 542 : return reader.ExitContainer(container);
79 : }
80 :
81 607 : CHIP_ERROR Load(PersistentStorageDelegate * storage) // NOLINT(bugprone-derived-method-shadowing-base-method)
82 : {
83 607 : CHIP_ERROR err = PersistableData::Load(storage);
84 1214 : return err.NoErrorIf(CHIP_ERROR_NOT_FOUND); // NOT_FOUND is OK; DataAccessor::Load already called Clear()
85 : }
86 : };
87 :
88 : template <class StorageId, class StorageData>
89 : struct EndpointEntryCount : public BaseEntryCount
90 : {
91 : using Serializer = DefaultSerializer<StorageId, StorageData>;
92 :
93 : EndpointId endpoint_id = kInvalidEndpointId;
94 :
95 607 : EndpointEntryCount(EndpointId endpoint, uint8_t count = 0) : BaseEntryCount(count), endpoint_id(endpoint) {}
96 607 : ~EndpointEntryCount() {}
97 :
98 897 : CHIP_ERROR UpdateKey(StorageKeyName & key) const override
99 : {
100 897 : VerifyOrReturnError(kInvalidEndpointId != endpoint_id, CHIP_ERROR_INVALID_ARGUMENT);
101 897 : key = Serializer::EndpointEntryCountKey(endpoint_id);
102 897 : return CHIP_NO_ERROR;
103 : }
104 : };
105 :
106 : // Prevent mutations from happening in TableEntryData::Serialize
107 : // If we just used a raw reference for TableEntryData::mEntry, C++ allows us
108 : // to mutate mEntry.mStorageId & mEntry.mStorageData in TableEntryData::Serialize
109 : // without having to do a const_cast; as an example, if we were to accidentally introduce
110 : // the following code in TableEntryData::Serialize (a const method):
111 : //
112 : // this->mEntry->mStorageData = StorageData();
113 : //
114 : // If TableEntryData::mEntry is a reference, it allows this with no compilation error;
115 : // But with ConstCorrectRef, we get a compile-time error that TableEntryData::mEntry->mStorageData
116 : // cannot be modified because it is a const value
117 : template <typename T>
118 : class ConstCorrectRef
119 : {
120 : T & mRef;
121 :
122 : public:
123 806 : inline ConstCorrectRef(T & ref) : mRef(ref) {}
124 :
125 : inline const T * operator->() const { return &mRef; }
126 : inline T * operator->() { return &mRef; }
127 :
128 412 : inline const T & operator*() const { return mRef; }
129 342 : inline T & operator*() { return mRef; }
130 : };
131 :
132 : template <class StorageId, class StorageData>
133 : struct TableEntryData : DataAccessor
134 : {
135 : using Serializer = DefaultSerializer<StorageId, StorageData>;
136 :
137 : EndpointId endpoint_id = kInvalidEndpointId;
138 : FabricIndex fabric_index = kUndefinedFabricIndex;
139 : EntryIndex index = 0;
140 : bool first = true;
141 : ConstCorrectRef<StorageId> storage_id;
142 : ConstCorrectRef<StorageData> storage_data;
143 :
144 403 : TableEntryData(EndpointId endpoint, FabricIndex fabric, StorageId & id, StorageData & data, EntryIndex idx = 0) :
145 403 : endpoint_id(endpoint), fabric_index(fabric), index(idx), storage_id(id), storage_data(data)
146 403 : {}
147 :
148 320 : CHIP_ERROR UpdateKey(StorageKeyName & key) const override
149 : {
150 320 : VerifyOrReturnError(kUndefinedFabricIndex != fabric_index, CHIP_ERROR_INVALID_FABRIC_INDEX);
151 320 : VerifyOrReturnError(kInvalidEndpointId != endpoint_id, CHIP_ERROR_INVALID_ARGUMENT);
152 320 : key = Serializer::FabricEntryKey(fabric_index, endpoint_id, index);
153 320 : return CHIP_NO_ERROR;
154 : }
155 :
156 114 : void Clear() override { Serializer::Clear(*storage_data); }
157 :
158 206 : CHIP_ERROR Serialize(TLV::TLVWriter & writer) const override
159 : {
160 : TLV::TLVType container;
161 206 : ReturnErrorOnFailure(writer.StartContainer(TLV::AnonymousTag(), TLV::kTLVType_Structure, container));
162 :
163 206 : ReturnErrorOnFailure(Serializer::SerializeId(writer, *storage_id));
164 :
165 206 : ReturnErrorOnFailure(Serializer::SerializeData(writer, *storage_data));
166 :
167 206 : return writer.EndContainer(container);
168 : }
169 :
170 114 : CHIP_ERROR Deserialize(TLV::TLVReader & reader) override
171 : {
172 114 : ReturnErrorOnFailure(reader.Next(TLV::kTLVType_Structure, TLV::AnonymousTag()));
173 :
174 : TLV::TLVType container;
175 114 : ReturnErrorOnFailure(reader.EnterContainer(container));
176 :
177 114 : ReturnErrorOnFailure(Serializer::DeserializeId(reader, *storage_id));
178 :
179 114 : ReturnErrorOnFailure(Serializer::DeserializeData(reader, *storage_data));
180 :
181 114 : return reader.ExitContainer(container);
182 : }
183 : };
184 :
185 : /**
186 : * @brief Class that holds a map to all entries in a fabric for a specific endpoint
187 : *
188 : * FabricEntryData is an access to a linked list of entries
189 : */
190 : template <class StorageId, class StorageData, size_t kEntryMaxBytes, size_t kFabricMaxBytes, uint16_t kMaxPerFabric>
191 : struct FabricEntryData : public PersistableData<kFabricMaxBytes>
192 : {
193 : using Serializer = DefaultSerializer<StorageId, StorageData>;
194 : using TypedTableEntryData = TableEntryData<StorageId, StorageData>;
195 : using Buffer = PersistenceBuffer<kEntryMaxBytes>;
196 : using TypedEndpointEntryCount = EndpointEntryCount<StorageId, StorageData>;
197 :
198 : EndpointId endpoint_id;
199 : FabricIndex fabric_index;
200 : uint8_t entry_count = 0;
201 : uint16_t max_per_fabric;
202 : uint16_t max_per_endpoint;
203 : StorageId entry_map[kMaxPerFabric];
204 :
205 1670 : FabricEntryData(EndpointId endpoint = kInvalidEndpointId, FabricIndex fabric = kUndefinedFabricIndex,
206 : uint16_t maxPerFabric = kMaxPerFabric, uint16_t maxPerEndpoint = Serializer::kMaxPerEndpoint()) :
207 1670 : endpoint_id(endpoint),
208 17638 : fabric_index(fabric), max_per_fabric(maxPerFabric), max_per_endpoint(maxPerEndpoint)
209 1670 : {}
210 :
211 1978 : CHIP_ERROR UpdateKey(StorageKeyName & key) const override
212 : {
213 1978 : VerifyOrReturnError(kUndefinedFabricIndex != fabric_index, CHIP_ERROR_INVALID_FABRIC_INDEX);
214 1978 : VerifyOrReturnError(kInvalidEndpointId != endpoint_id, CHIP_ERROR_INVALID_ARGUMENT);
215 1978 : key = Serializer::FabricEntryDataKey(fabric_index, endpoint_id);
216 1978 : return CHIP_NO_ERROR;
217 : }
218 :
219 1670 : void Clear() override
220 : {
221 1670 : entry_count = 0;
222 17946 : for (uint16_t i = 0; i < max_per_fabric; i++)
223 : {
224 16276 : entry_map[i].Clear();
225 : }
226 1670 : }
227 :
228 290 : CHIP_ERROR Serialize(TLV::TLVWriter & writer) const override
229 : {
230 : TLV::TLVType fabricEntryContainer;
231 290 : ReturnErrorOnFailure(writer.StartContainer(TLV::AnonymousTag(), TLV::kTLVType_Structure, fabricEntryContainer));
232 290 : ReturnErrorOnFailure(writer.Put(TLV::ContextTag(TagEntry::kEntryCount), entry_count));
233 :
234 : // Storing the entry map
235 : TLV::TLVType entryMapContainer;
236 290 : ReturnErrorOnFailure(
237 : writer.StartContainer(TLV::ContextTag(TagEntry::kStorageIdArray), TLV::kTLVType_Array, entryMapContainer));
238 2702 : for (uint16_t i = 0; i < max_per_fabric; i++)
239 : {
240 : TLV::TLVType entryIdContainer;
241 2412 : ReturnErrorOnFailure(writer.StartContainer(TLV::AnonymousTag(), TLV::kTLVType_Structure, entryIdContainer));
242 2412 : ReturnErrorOnFailure(Serializer::SerializeId(writer, entry_map[i]));
243 2412 : ReturnErrorOnFailure(writer.EndContainer(entryIdContainer));
244 : }
245 290 : ReturnErrorOnFailure(writer.EndContainer(entryMapContainer));
246 :
247 290 : return writer.EndContainer(fabricEntryContainer);
248 : }
249 :
250 : /// @brief This Deserialize method is implemented only to allow compilation. It is not used throughout the code.
251 : /// @param reader TLV reader
252 : /// @return CHIP_NO_ERROR
253 0 : CHIP_ERROR Deserialize(TLV::TLVReader & reader) override { return CHIP_ERROR_INCORRECT_STATE; }
254 :
255 : /// @brief This Deserialize method checks that the recovered entries from the deserialization fit in the current max and if
256 : /// there are too many entries in nvm, it deletes them. The method sets the deleted_entries output parameter to true if entries
257 : /// were deleted so that the load function can know it needs to save the Fabric entry data to update the entry_count and the
258 : /// entry map in stored memory.
259 : /// @param reade [in] TLV reader, must be big enough to hold the entry size
260 : /// @param storage [in] Persistent Storage Delegate, required to delete entries if the number of entries in storage is greater
261 : /// than the maximum allowed
262 : /// @param deleted_entries_count [out] uint8_t letting the caller (in this case the load method) know how many entries were
263 : /// deleted so it can adjust the fabric and global entry count accordingly. Even if Deserialize fails, this value will return
264 : /// the number of entries deleted before the failure happened.
265 : /// @return CHIP_NO_ERROR on success, specific CHIP_ERROR otherwise
266 841 : CHIP_ERROR Deserialize(TLV::TLVReader & reader, PersistentStorageDelegate & storage, uint8_t & deleted_entries_count)
267 : {
268 841 : ReturnErrorOnFailure(reader.Next(TLV::kTLVType_Structure, TLV::AnonymousTag()));
269 : TLV::TLVType fabricEntryContainer;
270 841 : ReturnErrorOnFailure(reader.EnterContainer(fabricEntryContainer));
271 841 : ReturnErrorOnFailure(reader.Next(TLV::ContextTag(TagEntry::kEntryCount)));
272 841 : ReturnErrorOnFailure(reader.Get(entry_count));
273 841 : entry_count = std::min(entry_count, static_cast<uint8_t>(max_per_fabric));
274 841 : ReturnErrorOnFailure(reader.Next(TLV::kTLVType_Array, TLV::ContextTag(TagEntry::kStorageIdArray)));
275 : TLV::TLVType entryMapContainer;
276 841 : ReturnErrorOnFailure(reader.EnterContainer(entryMapContainer));
277 :
278 841 : uint16_t i = 0;
279 : CHIP_ERROR err;
280 841 : deleted_entries_count = 0;
281 :
282 16878 : while ((err = reader.Next(TLV::AnonymousTag())) == CHIP_NO_ERROR)
283 : {
284 : TLV::TLVType entryIdContainer;
285 7598 : if (i < max_per_fabric)
286 : {
287 7588 : ReturnErrorOnFailure(reader.EnterContainer(entryIdContainer));
288 7588 : ReturnErrorOnFailure(Serializer::DeserializeId(reader, entry_map[i]));
289 7588 : ReturnErrorOnFailure(reader.ExitContainer(entryIdContainer));
290 : }
291 : else
292 : {
293 10 : StorageId unused;
294 10 : ReturnErrorOnFailure(reader.EnterContainer(entryIdContainer));
295 10 : ReturnErrorOnFailure(Serializer::DeserializeId(reader, unused));
296 10 : ReturnErrorOnFailure(reader.ExitContainer(entryIdContainer));
297 10 : ReturnErrorOnFailure(DeleteValue(storage, i));
298 10 : deleted_entries_count++;
299 : }
300 :
301 7598 : i++;
302 : }
303 :
304 1682 : VerifyOrReturnError(err == CHIP_END_OF_TLV, err);
305 841 : ReturnErrorOnFailure(reader.ExitContainer(entryMapContainer));
306 841 : return reader.ExitContainer(fabricEntryContainer);
307 : }
308 :
309 : /// @brief Finds the id of the entry with the specified index
310 : /// @return CHIP_NO_ERROR if managed to find the target entry, CHIP_ERROR_NOT_FOUND if not found
311 127 : CHIP_ERROR FindByIndex(PersistentStorageDelegate & storage, EntryIndex index, StorageId & entry_id)
312 : {
313 127 : VerifyOrReturnError(index < max_per_fabric, CHIP_ERROR_NOT_FOUND);
314 126 : VerifyOrReturnError(entry_map[index].IsValid(), CHIP_ERROR_NOT_FOUND);
315 66 : VerifyOrReturnError(kUndefinedFabricIndex != fabric_index, CHIP_ERROR_INVALID_FABRIC_INDEX);
316 66 : VerifyOrReturnError(kInvalidEndpointId != endpoint_id, CHIP_ERROR_INVALID_ARGUMENT);
317 66 : if (!storage.SyncDoesKeyExist(Serializer::FabricEntryKey(fabric_index, endpoint_id, index).KeyName()))
318 : {
319 0 : return CHIP_ERROR_NOT_FOUND;
320 : }
321 66 : entry_id = entry_map[index];
322 66 : return CHIP_NO_ERROR;
323 : }
324 :
325 : /// @brief Finds the index where the current entry should be inserted by going through the endpoint's table and checking
326 : /// whether the entry is already there. If the target is not in the table, sets idx to the first empty space.
327 : /// If the target was not found and the table is full, sets idx to kUndefinedEntryIndex.
328 : /// @param[in] target_entry StorageId of entry to find
329 : /// @param[out] idx Index where target or space is found.
330 : /// @return CHIP_NO_ERROR if managed to find the target entry, CHIP_ERROR_NOT_FOUND if not found and space left
331 : /// CHIP_ERROR_NO_MEMORY if target was not found and table is full
332 429 : CHIP_ERROR Find(const StorageId & target_entry, EntryIndex & idx)
333 : {
334 429 : EntryIndex firstFreeIdx = Data::kUndefinedEntryIndex; // storage index if entry not found
335 429 : uint16_t index = 0;
336 :
337 2756 : while (index < max_per_fabric)
338 : {
339 2541 : if (entry_map[index] == target_entry)
340 : {
341 214 : idx = index;
342 214 : return CHIP_NO_ERROR; // return entry at current index if entry found
343 : }
344 2327 : if (!entry_map[index].IsValid() && firstFreeIdx == Data::kUndefinedEntryIndex)
345 : {
346 275 : firstFreeIdx = index;
347 : }
348 2327 : index++;
349 : }
350 :
351 215 : if (firstFreeIdx < max_per_fabric)
352 : {
353 209 : idx = firstFreeIdx;
354 209 : return CHIP_ERROR_NOT_FOUND;
355 : }
356 6 : idx = Data::kUndefinedEntryIndex;
357 6 : return CHIP_ERROR_NO_MEMORY;
358 : }
359 :
360 210 : CHIP_ERROR SaveEntry(PersistentStorageDelegate & storage, const StorageId & id, const StorageData & data, Buffer & buffer)
361 : {
362 210 : CHIP_ERROR err = CHIP_NO_ERROR;
363 : // Look for empty storage space
364 :
365 210 : EntryIndex index = Data::kUndefinedEntryIndex;
366 210 : err = this->Find(id, index);
367 :
368 : // C++ doesn't have const constructors; variable is declared const
369 210 : const TypedTableEntryData entry(endpoint_id, fabric_index, const_cast<StorageId &>(id), const_cast<StorageData &>(data),
370 : index);
371 :
372 420 : if (CHIP_NO_ERROR == err)
373 : {
374 10 : return entry.Save(&storage, buffer.BufferSpan());
375 : }
376 :
377 400 : if (CHIP_ERROR_NOT_FOUND == err) // If not found, entry.index should be the first free index
378 : {
379 : // Update the global entry count
380 198 : TypedEndpointEntryCount endpoint_count(endpoint_id);
381 198 : ReturnErrorOnFailure(endpoint_count.Load(&storage));
382 198 : VerifyOrReturnError(endpoint_count.count_value < max_per_endpoint, CHIP_ERROR_NO_MEMORY);
383 196 : endpoint_count.count_value++;
384 196 : ReturnErrorOnFailure(endpoint_count.Save(&storage));
385 :
386 196 : entry_count++;
387 196 : entry_map[entry.index] = id;
388 :
389 196 : err = this->Save(&storage);
390 392 : if (CHIP_NO_ERROR != err)
391 : {
392 0 : endpoint_count.count_value--;
393 0 : ReturnErrorOnFailure(endpoint_count.Save(&storage));
394 0 : return err;
395 : }
396 :
397 196 : err = entry.Save(&storage, buffer.BufferSpan());
398 :
399 : // on failure to save the entry, undoes the changes to Fabric Entry Data
400 392 : if (CHIP_NO_ERROR != err)
401 : {
402 0 : endpoint_count.count_value--;
403 0 : ReturnErrorOnFailure(endpoint_count.Save(&storage));
404 :
405 0 : entry_count--;
406 0 : entry_map[entry.index].Clear();
407 0 : ReturnErrorOnFailure(this->Save(&storage));
408 0 : return err;
409 : }
410 198 : }
411 :
412 198 : return err;
413 210 : }
414 :
415 : /// @brief Removes an entry from the non-volatile memory and clears its index in the entry map. Decreases the number of entries
416 : /// in the global entry count and in the entry fabric data if successful. As the entry map size is not compressed upon removal,
417 : /// this only clears the entry corresponding to the entry from the entry map.
418 : /// @param storage Storage delegate to access the entry
419 : /// @param entry_id Entry to remove
420 : /// @return CHIP_NO_ERROR if successful, specific CHIP_ERROR otherwise
421 93 : CHIP_ERROR RemoveEntry(PersistentStorageDelegate & storage, const StorageId & entry_id)
422 : {
423 93 : CHIP_ERROR err = CHIP_NO_ERROR;
424 : EntryIndex entryIndex;
425 :
426 : // Empty Entry Fabric Data returns CHIP_NO_ERROR on remove
427 93 : if (entry_count > 0)
428 : {
429 : // If Find doesn't return CHIP_NO_ERROR, the entry wasn't found, which doesn't return an error
430 186 : VerifyOrReturnValue(this->Find(entry_id, entryIndex) == CHIP_NO_ERROR, CHIP_NO_ERROR);
431 :
432 : // Update the global entry count
433 92 : TypedEndpointEntryCount endpoint_entry_count(endpoint_id);
434 92 : ReturnErrorOnFailure(endpoint_entry_count.Load(&storage));
435 92 : endpoint_entry_count.count_value--;
436 92 : ReturnErrorOnFailure(endpoint_entry_count.Save(&storage));
437 :
438 92 : entry_count--;
439 92 : entry_map[entryIndex].Clear();
440 92 : err = this->Save(&storage);
441 :
442 : // On failure to update the entry map, undo the global count modification
443 184 : if (CHIP_NO_ERROR != err)
444 : {
445 0 : endpoint_entry_count.count_value++;
446 0 : ReturnErrorOnFailure(endpoint_entry_count.Save(&storage));
447 0 : return err;
448 : }
449 :
450 92 : err = DeleteValue(storage, entryIndex);
451 :
452 : // On failure to delete entry, undo the change to the Fabric Entry Data and the global entry count
453 184 : if (CHIP_NO_ERROR != err)
454 : {
455 0 : endpoint_entry_count.count_value++;
456 0 : ReturnErrorOnFailure(endpoint_entry_count.Save(&storage));
457 :
458 0 : entry_count++;
459 0 : entry_map[entryIndex] = entry_id;
460 0 : ReturnErrorOnFailure(this->Save(&storage));
461 0 : return err;
462 : }
463 92 : }
464 92 : return err;
465 : }
466 :
467 1670 : CHIP_ERROR Load(PersistentStorageDelegate * storage) // NOLINT(bugprone-derived-method-shadowing-base-method)
468 : {
469 1670 : VerifyOrReturnError(nullptr != storage, CHIP_ERROR_INVALID_ARGUMENT);
470 1670 : uint8_t deleted_entries_count = 0;
471 :
472 1670 : uint8_t buffer[kFabricMaxBytes] = { 0 };
473 1670 : StorageKeyName key = StorageKeyName::Uninitialized();
474 :
475 : // Set data to defaults
476 1670 : Clear();
477 :
478 : // Update storage key
479 1670 : ReturnErrorOnFailure(UpdateKey(key));
480 :
481 : // Load the serialized data
482 1670 : uint16_t size = static_cast<uint16_t>(sizeof(buffer));
483 1670 : CHIP_ERROR err = storage->SyncGetKeyValue(key.KeyName(), buffer, size);
484 3340 : VerifyOrReturnError(CHIP_ERROR_PERSISTED_STORAGE_VALUE_NOT_FOUND != err, CHIP_ERROR_NOT_FOUND);
485 841 : ReturnErrorOnFailure(err);
486 :
487 : // Decode serialized data
488 841 : TLV::TLVReader reader;
489 841 : reader.Init(buffer, size);
490 :
491 841 : err = Deserialize(reader, *storage, deleted_entries_count);
492 :
493 : // If Deserialize sets the "deleted_entries" variable, the table in flash memory held too many entries (can happen
494 : // if max_per_fabric was reduced during an OTA) and was adjusted during deserializing . The fabric data must then
495 : // be updated
496 841 : if (deleted_entries_count)
497 : {
498 2 : TypedEndpointEntryCount global_count(endpoint_id);
499 2 : ReturnErrorOnFailure(global_count.Load(storage));
500 2 : global_count.count_value = static_cast<uint8_t>(global_count.count_value - deleted_entries_count);
501 2 : ReturnErrorOnFailure(global_count.Save(storage));
502 2 : ReturnErrorOnFailure(this->Save(storage));
503 2 : }
504 :
505 841 : return err;
506 1670 : }
507 :
508 : private:
509 102 : CHIP_ERROR DeleteValue(PersistentStorageDelegate & storage, EntryIndex index)
510 : {
511 102 : StorageKeyName key = Serializer::FabricEntryKey(fabric_index, endpoint_id, index);
512 102 : return storage.SyncDeleteKeyValue(key.KeyName());
513 102 : }
514 : };
515 :
516 : template <class StorageId, class StorageData>
517 89 : CHIP_ERROR FabricTableImpl<StorageId, StorageData>::Init(PersistentStorageDelegate & storage)
518 : {
519 : // Verify the initialized parameter respects the maximum allowed values for entry capacity
520 89 : VerifyOrReturnError(mMaxPerFabric <= Serializer::kMaxPerFabric() && mMaxPerEndpoint <= Serializer::kMaxPerEndpoint(),
521 : CHIP_ERROR_INVALID_INTEGER_VALUE);
522 87 : this->mStorage = &storage;
523 87 : return CHIP_NO_ERROR;
524 : }
525 :
526 : template <class StorageId, class StorageData>
527 104 : void FabricTableImpl<StorageId, StorageData>::Finish()
528 104 : {}
529 :
530 : template <class StorageId, class StorageData>
531 147 : CHIP_ERROR FabricTableImpl<StorageId, StorageData>::GetFabricEntryCount(FabricIndex fabric_index, uint8_t & entry_count)
532 : {
533 : using TypedFabricEntryData = FabricEntryData<StorageId, StorageData, Serializer::kEntryMaxBytes(),
534 : Serializer::kFabricMaxBytes(), Serializer::kMaxPerFabric()>;
535 :
536 147 : VerifyOrReturnError(IsInitialized(), CHIP_ERROR_INTERNAL);
537 :
538 147 : TypedFabricEntryData fabric(mEndpointId, fabric_index);
539 147 : CHIP_ERROR err = fabric.Load(mStorage);
540 303 : VerifyOrReturnError(CHIP_NO_ERROR == err || CHIP_ERROR_NOT_FOUND == err, err);
541 :
542 294 : entry_count = (CHIP_ERROR_NOT_FOUND == err) ? 0 : fabric.entry_count;
543 :
544 147 : return CHIP_NO_ERROR;
545 147 : }
546 :
547 : template <class StorageId, class StorageData>
548 315 : CHIP_ERROR FabricTableImpl<StorageId, StorageData>::GetEndpointEntryCount(uint8_t & entry_count)
549 : {
550 : using TypedEndpointEntryCount = EndpointEntryCount<StorageId, StorageData>;
551 :
552 315 : VerifyOrReturnError(IsInitialized(), CHIP_ERROR_INTERNAL);
553 :
554 315 : TypedEndpointEntryCount endpoint_entry_count(mEndpointId);
555 :
556 315 : ReturnErrorOnFailure(endpoint_entry_count.Load(mStorage));
557 315 : entry_count = endpoint_entry_count.count_value;
558 :
559 315 : return CHIP_NO_ERROR;
560 315 : }
561 :
562 : template <class StorageId, class StorageData>
563 0 : CHIP_ERROR FabricTableImpl<StorageId, StorageData>::SetEndpointEntryCount(const uint8_t & entry_count)
564 : {
565 : using TypedEndpointEntryCount = EndpointEntryCount<StorageId, StorageData>;
566 0 : VerifyOrReturnError(IsInitialized(), CHIP_ERROR_INTERNAL);
567 :
568 0 : TypedEndpointEntryCount endpoint_entry_count(mEndpointId, entry_count);
569 0 : return endpoint_entry_count.Save(mStorage);
570 0 : }
571 :
572 : template <class StorageId, class StorageData>
573 290 : CHIP_ERROR FabricTableImpl<StorageId, StorageData>::GetRemainingCapacity(FabricIndex fabric_index, uint8_t & capacity)
574 : {
575 : using TypedFabricEntryData = FabricEntryData<StorageId, StorageData, Serializer::kEntryMaxBytes(),
576 : Serializer::kFabricMaxBytes(), Serializer::kMaxPerFabric()>;
577 :
578 290 : VerifyOrReturnError(IsInitialized(), CHIP_ERROR_INTERNAL);
579 :
580 290 : uint8_t endpoint_entry_count = 0;
581 290 : ReturnErrorOnFailure(GetEndpointEntryCount(endpoint_entry_count));
582 :
583 : // If the global entry count is higher than the maximal Global entry capacity, this returns a capacity of 0 until enough entries
584 : // have been deleted to bring the global number of entries under the global maximum.
585 290 : if (endpoint_entry_count > mMaxPerEndpoint)
586 : {
587 6 : capacity = 0;
588 6 : return CHIP_NO_ERROR;
589 : }
590 284 : uint8_t remaining_capacity_global = static_cast<uint8_t>(mMaxPerEndpoint - endpoint_entry_count);
591 284 : uint8_t remaining_capacity_fabric = static_cast<uint8_t>(mMaxPerFabric);
592 :
593 284 : TypedFabricEntryData fabric(mEndpointId, fabric_index);
594 :
595 : // Load fabric data (defaults to zero)TypedFabricEntryData
596 284 : CHIP_ERROR err = fabric.Load(mStorage);
597 624 : VerifyOrReturnError(CHIP_NO_ERROR == err || CHIP_ERROR_NOT_FOUND == err, err);
598 :
599 568 : if (err == CHIP_NO_ERROR)
600 : {
601 228 : remaining_capacity_fabric = static_cast<uint8_t>(mMaxPerFabric - fabric.entry_count);
602 : }
603 :
604 284 : capacity = std::min(remaining_capacity_fabric, remaining_capacity_global);
605 :
606 284 : return CHIP_NO_ERROR;
607 284 : }
608 :
609 : template <class StorageId, class StorageData>
610 : template <size_t kEntryMaxBytes>
611 210 : CHIP_ERROR FabricTableImpl<StorageId, StorageData>::SetTableEntry(FabricIndex fabric_index, const StorageId & id,
612 : const StorageData & data,
613 : PersistenceBuffer<kEntryMaxBytes> & writeBuffer)
614 : {
615 : using TypedFabricEntryData = FabricEntryData<StorageId, StorageData, Serializer::kEntryMaxBytes(),
616 : Serializer::kFabricMaxBytes(), Serializer::kMaxPerFabric()>;
617 :
618 210 : VerifyOrReturnError(IsInitialized(), CHIP_ERROR_INTERNAL);
619 :
620 210 : TypedFabricEntryData fabric(mEndpointId, fabric_index, mMaxPerFabric, mMaxPerEndpoint);
621 :
622 : // Load fabric data (defaults to zero)
623 210 : CHIP_ERROR err = fabric.Load(mStorage);
624 469 : VerifyOrReturnError(CHIP_NO_ERROR == err || CHIP_ERROR_NOT_FOUND == err, err);
625 :
626 210 : err = fabric.SaveEntry(*mStorage, id, data, writeBuffer);
627 210 : return err;
628 210 : }
629 :
630 : template <class StorageId, class StorageData>
631 : template <size_t kEntryMaxBytes>
632 189 : CHIP_ERROR FabricTableImpl<StorageId, StorageData>::GetTableEntry(FabricIndex fabric_index, StorageId & entry_id,
633 : StorageData & data, PersistenceBuffer<kEntryMaxBytes> & buffer)
634 : {
635 : using TypedFabricEntryData = FabricEntryData<StorageId, StorageData, Serializer::kEntryMaxBytes(),
636 : Serializer::kFabricMaxBytes(), Serializer::kMaxPerFabric()>;
637 189 : VerifyOrReturnError(IsInitialized(), CHIP_ERROR_INTERNAL);
638 :
639 189 : TypedFabricEntryData fabric(mEndpointId, fabric_index, mMaxPerFabric, mMaxPerEndpoint);
640 189 : TableEntryData<StorageId, StorageData> table_entry(mEndpointId, fabric_index, entry_id, data);
641 :
642 189 : ReturnErrorOnFailure(fabric.Load(mStorage));
643 244 : VerifyOrReturnError(fabric.Find(entry_id, table_entry.index) == CHIP_NO_ERROR, CHIP_ERROR_NOT_FOUND);
644 :
645 110 : CHIP_ERROR err = table_entry.Load(mStorage, buffer.BufferSpan());
646 :
647 : // If entry.Load returns "buffer too small", the entry in memory is too big to be retrieved (this could happen if the
648 : // kEntryMaxBytes was reduced by OTA) and therefore must be deleted as is is no longer considered accessible.
649 220 : if (err == CHIP_ERROR_BUFFER_TOO_SMALL)
650 : {
651 0 : ReturnErrorOnFailure(this->RemoveTableEntry(fabric_index, entry_id));
652 : }
653 110 : ReturnErrorOnFailure(err);
654 :
655 110 : return CHIP_NO_ERROR;
656 189 : }
657 :
658 : template <class StorageId, class StorageData>
659 4 : CHIP_ERROR FabricTableImpl<StorageId, StorageData>::FindTableEntry(FabricIndex fabric_index, const StorageId & entry_id,
660 : EntryIndex & idx)
661 : {
662 : using TypedFabricEntryData = FabricEntryData<StorageId, StorageData, Serializer::kEntryMaxBytes(),
663 : Serializer::kFabricMaxBytes(), Serializer::kMaxPerFabric()>;
664 4 : VerifyOrReturnError(IsInitialized(), CHIP_ERROR_INTERNAL);
665 :
666 4 : TypedFabricEntryData fabric(mEndpointId, fabric_index, mMaxPerFabric, mMaxPerEndpoint);
667 :
668 4 : ReturnErrorOnFailure(fabric.Load(mStorage));
669 8 : VerifyOrReturnError(fabric.Find(entry_id, idx) == CHIP_NO_ERROR, CHIP_ERROR_NOT_FOUND);
670 :
671 2 : return CHIP_NO_ERROR;
672 4 : }
673 :
674 : template <class StorageId, class StorageData>
675 18 : CHIP_ERROR FabricTableImpl<StorageId, StorageData>::RemoveTableEntry(FabricIndex fabric_index, const StorageId & entry_id)
676 : {
677 : using TypedFabricEntryData = FabricEntryData<StorageId, StorageData, Serializer::kEntryMaxBytes(),
678 : Serializer::kFabricMaxBytes(), Serializer::kMaxPerFabric()>;
679 :
680 18 : VerifyOrReturnError(IsInitialized(), CHIP_ERROR_INTERNAL);
681 18 : TypedFabricEntryData fabric(mEndpointId, fabric_index, mMaxPerFabric, mMaxPerEndpoint);
682 :
683 18 : ReturnErrorOnFailure(fabric.Load(mStorage));
684 :
685 18 : return fabric.RemoveEntry(*mStorage, entry_id);
686 18 : }
687 :
688 : /// @brief This function is meant to provide a way to empty the entry table without knowing any specific entry Id. Outside of this
689 : /// specific use case, RemoveTableEntry should be used.
690 : /// @param fabric_index Fabric in which the entry belongs
691 : /// @param entry_idx Position in the Table
692 : /// @return CHIP_NO_ERROR if removal was successful, errors if failed to remove the entry or to update the fabric after removing it
693 : template <class StorageId, class StorageData>
694 127 : CHIP_ERROR FabricTableImpl<StorageId, StorageData>::RemoveTableEntryAtPosition(EndpointId endpoint, FabricIndex fabric_index,
695 : EntryIndex entry_idx)
696 : {
697 : using TypedFabricEntryData = FabricEntryData<StorageId, StorageData, Serializer::kEntryMaxBytes(),
698 : Serializer::kFabricMaxBytes(), Serializer::kMaxPerFabric()>;
699 :
700 127 : VerifyOrReturnError(IsInitialized(), CHIP_ERROR_INTERNAL);
701 :
702 127 : TypedFabricEntryData fabric(endpoint, fabric_index, mMaxPerFabric, mMaxPerEndpoint);
703 :
704 127 : ReturnErrorOnFailure(fabric.Load(mStorage));
705 127 : StorageId entryId;
706 127 : CHIP_ERROR err = fabric.FindByIndex(*mStorage, entry_idx, entryId);
707 254 : VerifyOrReturnValue(CHIP_ERROR_NOT_FOUND != err, CHIP_NO_ERROR);
708 66 : ReturnErrorOnFailure(err);
709 :
710 66 : return fabric.RemoveEntry(*mStorage, entryId);
711 127 : }
712 :
713 : template <class StorageId, class StorageData>
714 46 : CHIP_ERROR FabricTableImpl<StorageId, StorageData>::RemoveFabric(DataModel::ProviderMetadataTree & provider,
715 : FabricIndex fabric_index)
716 : {
717 : using TypedFabricEntryData = FabricEntryData<StorageId, StorageData, Serializer::kEntryMaxBytes(),
718 : Serializer::kFabricMaxBytes(), Serializer::kMaxPerFabric()>;
719 :
720 46 : VerifyOrReturnError(IsInitialized(), CHIP_ERROR_INTERNAL);
721 :
722 46 : ReadOnlyBufferBuilder<DataModel::EndpointEntry> endpointsBuilder;
723 46 : ReturnErrorOnFailure(provider.Endpoints(endpointsBuilder));
724 :
725 364 : for (const auto & ep : endpointsBuilder.TakeBuffer())
726 : {
727 159 : EndpointId endpoint = ep.id;
728 159 : TypedFabricEntryData fabric(endpoint, fabric_index);
729 159 : EntryIndex idx = 0;
730 159 : CHIP_ERROR err = fabric.Load(mStorage);
731 462 : VerifyOrReturnError(CHIP_NO_ERROR == err || CHIP_ERROR_NOT_FOUND == err, err);
732 318 : if (CHIP_ERROR_NOT_FOUND == err)
733 : {
734 144 : continue;
735 : }
736 :
737 114 : while (idx < mMaxPerFabric)
738 : {
739 99 : err = RemoveTableEntryAtPosition(endpoint, fabric_index, idx);
740 198 : VerifyOrReturnError(CHIP_NO_ERROR == err || CHIP_ERROR_NOT_FOUND == err, err);
741 99 : idx++;
742 : }
743 :
744 : // Remove fabric entries on endpoint
745 15 : ReturnErrorOnFailure(fabric.Delete(mStorage));
746 : }
747 :
748 46 : return CHIP_NO_ERROR;
749 46 : }
750 :
751 : template <class StorageId, class StorageData>
752 2 : CHIP_ERROR FabricTableImpl<StorageId, StorageData>::RemoveEndpoint()
753 : {
754 : using TypedFabricEntryData = FabricEntryData<StorageId, StorageData, Serializer::kEntryMaxBytes(),
755 : Serializer::kFabricMaxBytes(), Serializer::kMaxPerFabric()>;
756 :
757 2 : VerifyOrReturnError(IsInitialized(), CHIP_ERROR_INTERNAL);
758 :
759 1014 : for (FabricIndex fabric_index = kMinValidFabricIndex; fabric_index < kMaxValidFabricIndex; fabric_index++)
760 : {
761 506 : TypedFabricEntryData fabric(mEndpointId, fabric_index);
762 506 : CHIP_ERROR err = fabric.Load(mStorage);
763 1515 : VerifyOrReturnError(CHIP_NO_ERROR == err || CHIP_ERROR_NOT_FOUND == err, err);
764 1012 : if (CHIP_ERROR_NOT_FOUND == err)
765 : {
766 503 : continue;
767 : }
768 :
769 3 : EntryIndex idx = 0;
770 28 : while (idx < mMaxPerFabric)
771 : {
772 25 : err = RemoveTableEntryAtPosition(mEndpointId, fabric_index, idx);
773 50 : VerifyOrReturnError(CHIP_NO_ERROR == err || CHIP_ERROR_NOT_FOUND == err, err);
774 25 : idx++;
775 : };
776 :
777 : // Remove fabric entries on endpoint
778 3 : ReturnErrorOnFailure(fabric.Delete(mStorage));
779 : }
780 :
781 2 : return CHIP_NO_ERROR;
782 : }
783 :
784 : template <class StorageId, class StorageData>
785 95 : void FabricTableImpl<StorageId, StorageData>::SetEndpoint(EndpointId endpoint)
786 : {
787 95 : mEndpointId = endpoint;
788 95 : }
789 :
790 : template <class StorageId, class StorageData>
791 52 : void FabricTableImpl<StorageId, StorageData>::SetTableSize(uint16_t endpointTableSize, uint16_t maxPerFabric)
792 : {
793 : // Verify the endpoint passed size respects the limits of the device configuration
794 52 : VerifyOrDie(Serializer::kMaxPerFabric() > 0);
795 52 : VerifyOrDie(Serializer::kMaxPerEndpoint() > 0);
796 52 : mMaxPerEndpoint = std::min(Serializer::kMaxPerEndpoint(), endpointTableSize);
797 52 : mMaxPerFabric = std::min(endpointTableSize, std::min(Serializer::kMaxPerFabric(), maxPerFabric));
798 52 : }
799 :
800 : template <class StorageId, class StorageData>
801 : template <size_t kEntryMaxBytes, class UnaryFunc>
802 2 : CHIP_ERROR FabricTableImpl<StorageId, StorageData>::IterateEntries(FabricIndex fabric, PersistenceBuffer<kEntryMaxBytes> & buffer,
803 : UnaryFunc iterateFn)
804 : {
805 2 : VerifyOrReturnError(IsInitialized(), CHIP_ERROR_INTERNAL);
806 :
807 2 : EntryIteratorImpl<kEntryMaxBytes> iterator(*this, fabric, mEndpointId, mMaxPerFabric, mMaxPerEndpoint, buffer);
808 2 : return iterateFn(iterator);
809 2 : }
810 :
811 : template <class StorageId, class StorageData>
812 : template <size_t kEntryMaxBytes>
813 2 : FabricTableImpl<StorageId, StorageData>::EntryIteratorImpl<kEntryMaxBytes>::EntryIteratorImpl(
814 : FabricTableImpl & provider, FabricIndex fabricIdx, EndpointId endpoint, uint16_t maxPerFabric, uint16_t maxPerEndpoint,
815 : PersistenceBuffer<kEntryMaxBytes> & buffer) :
816 2 : mProvider(provider),
817 2 : mBuffer(buffer), mFabric(fabricIdx), mEndpoint(endpoint), mMaxPerFabric(maxPerFabric), mMaxPerEndpoint(maxPerEndpoint)
818 : {
819 : using TypedFabricEntryData = FabricEntryData<StorageId, StorageData, Serializer::kEntryMaxBytes(),
820 : Serializer::kFabricMaxBytes(), Serializer::kMaxPerFabric()>;
821 :
822 2 : TypedFabricEntryData fabric(mEndpoint, fabricIdx, mMaxPerFabric, mMaxPerEndpoint);
823 2 : ReturnOnFailure(fabric.Load(provider.mStorage));
824 2 : mTotalEntries = fabric.entry_count;
825 2 : mEntryIndex = 0;
826 2 : }
827 :
828 : template <class StorageId, class StorageData>
829 : template <size_t kEntryMaxBytes>
830 0 : size_t FabricTableImpl<StorageId, StorageData>::EntryIteratorImpl<kEntryMaxBytes>::Count()
831 : {
832 0 : return mTotalEntries;
833 : }
834 :
835 : template <class StorageId, class StorageData>
836 : template <size_t kEntryMaxBytes>
837 6 : bool FabricTableImpl<StorageId, StorageData>::EntryIteratorImpl<kEntryMaxBytes>::Next(TableEntry & output)
838 : {
839 : using TypedFabricEntryData = FabricEntryData<StorageId, StorageData, Serializer::kEntryMaxBytes(),
840 : Serializer::kFabricMaxBytes(), Serializer::kMaxPerFabric()>;
841 :
842 6 : TypedFabricEntryData fabric(mEndpoint, mFabric);
843 :
844 12 : VerifyOrReturnError(fabric.Load(mProvider.mStorage) == CHIP_NO_ERROR, false);
845 :
846 : // looks for next available entry
847 12 : while (mEntryIndex < mMaxPerFabric)
848 : {
849 10 : if (fabric.entry_map[mEntryIndex].IsValid())
850 : {
851 4 : TableEntryData<StorageId, StorageData> entry(mEndpoint, mFabric, output.mStorageId, output.mStorageData, mEntryIndex);
852 8 : VerifyOrReturnError(entry.Load(mProvider.mStorage, mBuffer.BufferSpan()) == CHIP_NO_ERROR, false);
853 4 : mEntryIndex++;
854 :
855 4 : return true;
856 4 : }
857 :
858 6 : mEntryIndex++;
859 : }
860 :
861 2 : return false;
862 6 : }
863 :
864 : template <class StorageId, class StorageData>
865 : template <size_t kEntryMaxBytes>
866 0 : void FabricTableImpl<StorageId, StorageData>::EntryIteratorImpl<kEntryMaxBytes>::Release()
867 0 : {}
868 : } // namespace Storage
869 : } // namespace app
870 : } // namespace chip
|