Matter SDK Coverage Report
Current view: top level - app/persistence - AttributePersistenceMigration.cpp (source / functions) Coverage Total Hit
Test: SHA:3f9cd168e84cd831b7699126f5296f5c5498690f Lines: 94.6 % 37 35
Test Date: 2026-04-27 19:52:19 Functions: 100.0 % 3 3

            Line data    Source code
       1              : /*
       2              :  *    Copyright (c) 2026 Project CHIP Authors
       3              :  *
       4              :  *    Licensed under the Apache License, Version 2.0 (the "License");
       5              :  *    you may not use this file except in compliance with the License.
       6              :  *    You may obtain a copy of the License at
       7              :  *
       8              :  *        http://www.apache.org/licenses/LICENSE-2.0
       9              :  *
      10              :  *    Unless required by applicable law or agreed to in writing, software
      11              :  *    distributed under the License is distributed on an "AS IS" BASIS,
      12              :  *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
      13              :  *    See the License for the specific language governing permissions and
      14              :  *    limitations under the License.
      15              :  */
      16              : 
      17              : #include <app/persistence/AttributePersistenceMigration.h>
      18              : #include <lib/core/CHIPEncoding.h>
      19              : #include <lib/support/logging/CHIPLogging.h>
      20              : #include <nlbyteorder.h>
      21              : 
      22              : #include <cstring>
      23              : 
      24              : namespace {
      25              : 
      26              : using namespace ::chip;
      27              : using namespace chip::app;
      28              : 
      29              : /**
      30              :  * Validates that the given size is a supported scalar width (1, 2, 4, or 8 bytes).
      31              :  * On big-endian systems, also performs an in-place little-endian to host byte-order swap.
      32              :  * On little-endian systems, no swap is needed so only the size validation is performed.
      33              :  */
      34            8 : CHIP_ERROR HostSwapBySize(uint8_t * data, size_t size)
      35              : {
      36            8 :     switch (size)
      37              :     {
      38              : #if (NLBYTEORDER == NLBYTEORDER_BIG_ENDIAN)
      39              :     case 1:
      40              :         // Single byte, no swap needed.
      41              :         return CHIP_NO_ERROR;
      42              :     case 2: {
      43              :         uint16_t val;
      44              :         memcpy(&val, data, sizeof(val));
      45              :         val = chip::Encoding::LittleEndian::HostSwap16(val);
      46              :         memcpy(data, &val, sizeof(val));
      47              :         return CHIP_NO_ERROR;
      48              :     }
      49              :     case 4: {
      50              :         uint32_t val;
      51              :         memcpy(&val, data, sizeof(val));
      52              :         val = chip::Encoding::LittleEndian::HostSwap32(val);
      53              :         memcpy(data, &val, sizeof(val));
      54              :         return CHIP_NO_ERROR;
      55              :     }
      56              :     case 8: {
      57              :         uint64_t val;
      58              :         memcpy(&val, data, sizeof(val));
      59              :         val = chip::Encoding::LittleEndian::HostSwap64(val);
      60              :         memcpy(data, &val, sizeof(val));
      61              :         return CHIP_NO_ERROR;
      62              :     }
      63              : #else
      64            7 :     case 1:
      65              :     case 2:
      66              :     case 4:
      67              :     case 8:
      68            7 :         return CHIP_NO_ERROR;
      69              : #endif
      70            1 :     default:
      71            1 :         return CHIP_ERROR_INVALID_ARGUMENT;
      72              :     }
      73              : }
      74              : 
      75           11 : CHIP_ERROR MigrateValueFromSafe(const ConcreteAttributePath & attrPath, SafeAttributePersistenceProvider & provider,
      76              :                                 MutableByteSpan & buffer, size_t valueSize, bool isScalar)
      77              : {
      78              : 
      79           11 :     VerifyOrReturnError(valueSize <= buffer.size(), CHIP_ERROR_BUFFER_TOO_SMALL);
      80              : 
      81              : #if (NLBYTEORDER == NLBYTEORDER_LITTLE_ENDIAN)
      82              :     // On little-endian systems, no byte swap is needed for scalar values.
      83              :     // Read directly into the output buffer and validate the size.
      84           11 :     ReturnErrorOnFailure(provider.SafeReadValue(attrPath, buffer));
      85            9 :     VerifyOrReturnError(buffer.size() == valueSize, CHIP_ERROR_INCORRECT_STATE);
      86              :     // For scalar values in LE the function HostSwapBySize just checks that a valid size was provided.
      87            9 :     if (isScalar)
      88              :     {
      89            8 :         ReturnErrorOnFailure(HostSwapBySize(buffer.data(), valueSize));
      90              :     }
      91              : #else
      92              : 
      93              :     // On big-endian, no need to swap non-scalar values.
      94              :     // For non-scalar values, just return the raw data.
      95              :     if (!isScalar)
      96              :     {
      97              :         return provider.SafeReadValue(attrPath, buffer);
      98              :     }
      99              : 
     100              :     // For scalar values, read into a temp buffer, byte-swap, then copy out.
     101              :     uint8_t attrData[sizeof(uint64_t)];
     102              :     MutableByteSpan tempVal(attrData);
     103              : 
     104              :     ReturnErrorOnFailure(provider.SafeReadValue(attrPath, tempVal));
     105              :     VerifyOrReturnError(tempVal.size() == valueSize, CHIP_ERROR_INCORRECT_STATE);
     106              :     ReturnErrorOnFailure(HostSwapBySize(tempVal.data(), valueSize));
     107              :     memcpy(buffer.data(), tempVal.data(), valueSize);
     108              :     buffer.reduce_size(valueSize);
     109              : #endif
     110            8 :     return CHIP_NO_ERROR;
     111              : }
     112              : } // namespace
     113              : namespace chip::app {
     114              : 
     115           10 : CHIP_ERROR MigrateFromSafeToAttributePersistenceProvider(SafeAttributePersistenceProvider & safeProvider,
     116              :                                                          AttributePersistenceProvider & dstProvider,
     117              :                                                          const ConcreteClusterPath & cluster,
     118              :                                                          Span<const AttrMigrationData> attributes, MutableByteSpan buffer)
     119              : {
     120           10 :     bool hadMigrationErrors = false;
     121           10 :     ConcreteAttributePath attrPath;
     122              : 
     123           22 :     for (const auto & entry : attributes)
     124              :     {
     125           12 :         attrPath = ConcreteAttributePath(cluster.mEndpointId, cluster.mClusterId, entry.attributeId);
     126              : 
     127              :         // Create a copy of the buffer to check if the value is already in the AttributePersistence.
     128              :         // If the attribute value is already stored in AttributePersistence, skip it.
     129              :         // Note: we assume any other error (e.g. including buffer too small) to be an indication that
     130              :         //       the key exists. The only case we migrate is if we are explicitly told "not found".
     131           12 :         MutableByteSpan readAttrBuffer = buffer;
     132           24 :         if (dstProvider.ReadValue(attrPath, readAttrBuffer) != CHIP_ERROR_PERSISTED_STORAGE_VALUE_NOT_FOUND)
     133              :         {
     134            4 :             continue;
     135              :         }
     136              : 
     137              :         // We make a copy of the buffer so it can be resized
     138              :         // Still refers to same internal buffer though
     139              :         // Read value from the safe provider, will resize copyOfBuffer to read size
     140           11 :         MutableByteSpan copyOfBuffer = buffer;
     141              :         // If there was an error reading from SafeAttributePersistence, then we shouldn't try to write that value
     142              :         // to AttributePersistence
     143              :         ChipError attributeMigrationError =
     144           11 :             MigrateValueFromSafe(attrPath, safeProvider, copyOfBuffer, entry.valueSize, entry.isScalar);
     145           22 :         if (attributeMigrationError != CHIP_NO_ERROR)
     146              :         {
     147              :             // If the value was not found in SafeAttributePersistence, it means that it was already migrated or that
     148              :             // there wasn't a value stored for this attribute in the first place, so we skip it.
     149            6 :             if (attributeMigrationError != CHIP_ERROR_PERSISTED_STORAGE_VALUE_NOT_FOUND)
     150              :             {
     151            1 :                 hadMigrationErrors = true;
     152            1 :                 ChipLogError(DataManagement,
     153              :                              "AttributeMigration: Error reading SafeAttribute '" ChipLogFormatMEI
     154              :                              "' from cluster '" ChipLogFormatMEI "' (err=%" CHIP_ERROR_FORMAT ")",
     155              :                              ChipLogValueMEI(entry.attributeId), ChipLogValueMEI(cluster.mClusterId),
     156              :                              attributeMigrationError.Format());
     157              :             }
     158            3 :             continue;
     159              :         }
     160              : 
     161              :         // Delete from the safe provider immediately after a successful read, so we only try to
     162              :         // migrate the persisted values once and avoid re-migrating after a reset.
     163            8 :         attributeMigrationError = safeProvider.SafeDeleteValue(attrPath);
     164           16 :         if (attributeMigrationError != CHIP_NO_ERROR)
     165              :         {
     166            0 :             hadMigrationErrors = true;
     167            0 :             ChipLogError(DataManagement,
     168              :                          "AttributeMigration: Error deleting SafeAttribute '" ChipLogFormatMEI "' from cluster '" ChipLogFormatMEI
     169              :                          "' (err=%" CHIP_ERROR_FORMAT ")",
     170              :                          ChipLogValueMEI(entry.attributeId), ChipLogValueMEI(cluster.mClusterId), attributeMigrationError.Format());
     171              :         }
     172              : 
     173              :         // Write value from SafeAttributePersistence into AttributePersistence
     174            8 :         attributeMigrationError = dstProvider.WriteValue(attrPath, copyOfBuffer);
     175           16 :         if (attributeMigrationError != CHIP_NO_ERROR)
     176              :         {
     177            1 :             hadMigrationErrors = true;
     178            1 :             ChipLogError(DataManagement,
     179              :                          "AttributeMigration: Error writing Attribute '" ChipLogFormatMEI "' from cluster '" ChipLogFormatMEI
     180              :                          "' (err=%" CHIP_ERROR_FORMAT ")",
     181              :                          ChipLogValueMEI(entry.attributeId), ChipLogValueMEI(cluster.mClusterId), attributeMigrationError.Format());
     182              :         }
     183              :     }
     184           10 :     return hadMigrationErrors ? CHIP_ERROR_HAD_FAILURES : CHIP_NO_ERROR;
     185              : }
     186              : } // namespace chip::app
        

Generated by: LCOV version 2.0-1