blob: ab8d59035c0b9c7afebe15c15809343f7da4c2d6 [file] [log] [blame]
/*
* Copyright (c) 2026 Project CHIP Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include <app/persistence/AttributePersistenceMigration.h>
#include <lib/support/logging/CHIPLogging.h>
namespace chip::app {
CHIP_ERROR MigrateFromSafeToAttributePersistenceProvider(SafeAttributePersistenceProvider & safeProvider,
AttributePersistenceProvider & dstProvider,
const ConcreteClusterPath & cluster,
Span<const AttrMigrationData> attributes, MutableByteSpan buffer)
{
bool hadMigrationErrors = false;
ConcreteAttributePath attrPath;
for (const auto & entry : attributes)
{
attrPath = ConcreteAttributePath(cluster.mEndpointId, cluster.mClusterId, entry.attributeId);
// Create a copy of the buffer to check if the value is already in the AttributePersistence.
// If the attribute value is already stored in AttributePersistence, skip it.
// Note: we assume any other error (e.g. including buffer too small) to be an indication that
// the key exists. The only case we migrate is if we are explicitly told "not found".
MutableByteSpan readAttrBuffer = buffer;
if (dstProvider.ReadValue(attrPath, readAttrBuffer) != CHIP_ERROR_PERSISTED_STORAGE_VALUE_NOT_FOUND)
{
continue;
}
// Verify that the migrator provided is not null.
if (entry.migrator == nullptr)
{
hadMigrationErrors = true;
ChipLogError(DataManagement,
"AttributeMigration: Null migrator for attribute '" ChipLogFormatMEI "' from cluster '" ChipLogFormatMEI
"'",
ChipLogValueMEI(entry.attributeId), ChipLogValueMEI(cluster.mClusterId));
continue;
}
// We make a copy of the buffer so it can be resized
// Still refers to same internal buffer though
// Read value from the safe provider, will resize copyOfBuffer to read size
MutableByteSpan copyOfBuffer = buffer;
// If there was an error reading from SafeAttributePersistence, then we shouldn't try to write that value
// to AttributePersistence
ChipError attributeMigrationError = entry.migrator(attrPath, safeProvider, copyOfBuffer);
if (attributeMigrationError != CHIP_NO_ERROR)
{
// If the value was not found in SafeAttributePersistence, it means that it was already migrated or that
// there wasn't a value stored for this attribute in the first place, so we skip it.
if (attributeMigrationError != CHIP_ERROR_PERSISTED_STORAGE_VALUE_NOT_FOUND)
{
hadMigrationErrors = true;
ChipLogError(DataManagement,
"AttributeMigration: Error reading SafeAttribute '" ChipLogFormatMEI
"' from cluster '" ChipLogFormatMEI "' (err=%" CHIP_ERROR_FORMAT ")",
ChipLogValueMEI(entry.attributeId), ChipLogValueMEI(cluster.mClusterId),
attributeMigrationError.Format());
}
continue;
}
// Delete from the safe provider immediately after a successful read, so we only try to
// migrate the persisted values once and avoid re-migrating after a reset.
attributeMigrationError = safeProvider.SafeDeleteValue(attrPath);
if (attributeMigrationError != CHIP_NO_ERROR)
{
hadMigrationErrors = true;
ChipLogError(DataManagement,
"AttributeMigration: Error deleting SafeAttribute '" ChipLogFormatMEI "' from cluster '" ChipLogFormatMEI
"' (err=%" CHIP_ERROR_FORMAT ")",
ChipLogValueMEI(entry.attributeId), ChipLogValueMEI(cluster.mClusterId), attributeMigrationError.Format());
}
// Write value from SafeAttributePersistence into AttributePersistence
attributeMigrationError = dstProvider.WriteValue(attrPath, copyOfBuffer);
if (attributeMigrationError != CHIP_NO_ERROR)
{
hadMigrationErrors = true;
ChipLogError(DataManagement,
"AttributeMigration: Error writing Attribute '" ChipLogFormatMEI "' from cluster '" ChipLogFormatMEI
"' (err=%" CHIP_ERROR_FORMAT ")",
ChipLogValueMEI(entry.attributeId), ChipLogValueMEI(cluster.mClusterId), attributeMigrationError.Format());
}
}
return hadMigrationErrors ? CHIP_ERROR_HAD_FAILURES : CHIP_NO_ERROR;
}
namespace DefaultMigrators {
CHIP_ERROR SafeValue(const ConcreteAttributePath & attrPath, SafeAttributePersistenceProvider & provider, MutableByteSpan & buffer)
{
return provider.SafeReadValue(attrPath, buffer);
}
} // namespace DefaultMigrators
} // namespace chip::app