blob: f5c0860d133be4b908b44575b5dce7f00bbe5ebb [file] [log] [blame]
// Copyright 2021 The Pigweed 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
//
// https://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 "pw_software_update/update_manager.h"
#include <string_view>
#include "pw_software_update/manifest_accessor.h"
#include "pw_software_update/update_bundle.pwpb.h"
namespace pw::software_update {
Status BundledUpdateManager::ApplyUpdate() {
PW_LOG_DEBUG("Attempting to apply the update");
protobuf::StringToMessageMap signed_targets_metadata_map =
bundle_.GetDecoder().AsStringToMessageMap(static_cast<uint32_t>(
pw::software_update::UpdateBundle::Fields::TARGETS_METADATA));
if (const Status status = signed_targets_metadata_map.status();
!status.ok()) {
PW_LOG_ERROR("Update bundle does not contain the targets_metadata map: %d",
static_cast<int>(status.code()));
return status;
}
// There should only be one element in the map, which is the top-level
// targets metadata.
constexpr std::string_view kTopLevelTargetsName = "targets";
protobuf::Message signed_targets_metadata =
signed_targets_metadata_map[kTopLevelTargetsName];
if (const Status status = signed_targets_metadata.status(); !status.ok()) {
PW_LOG_ERROR(
"The targets_metadata map does not contain the targets entry: %d",
static_cast<int>(status.code()));
return status;
}
protobuf::Message targets_metadata = signed_targets_metadata.AsMessage(
static_cast<uint32_t>(pw::software_update::SignedTargetsMetadata::Fields::
SERIALIZED_TARGETS_METADATA));
if (const Status status = targets_metadata.status(); !status.ok()) {
PW_LOG_ERROR(
"The targets targets_metadata entry does not contain the "
"serialized_target_metadata: %d",
static_cast<int>(status.code()));
return status;
}
protobuf::RepeatedMessages target_files =
targets_metadata.AsRepeatedMessages(static_cast<uint32_t>(
pw::software_update::TargetsMetadata::Fields::TARGET_FILES));
if (const Status status = target_files.status(); !status.ok()) {
PW_LOG_ERROR(
"The serialized_target_metadata does not contain target_files: %d",
static_cast<int>(status.code()));
return status;
}
for (pw::protobuf::Message file_name : target_files) {
// TODO: Use a config.h parameter for this.
constexpr size_t kFileNameMaxSize = 32;
std::array<std::byte, kFileNameMaxSize> buf = {};
protobuf::String name = file_name.AsString(static_cast<uint32_t>(
pw::software_update::TargetFile::Fields::FILE_NAME));
PW_TRY(name.status());
const Result<ByteSpan> read_result = name.GetBytesReader().Read(buf);
PW_TRY(read_result.status());
const ConstByteSpan file_name_span = read_result.value();
const std::string_view file_name_view(
reinterpret_cast<const char*>(file_name_span.data()),
file_name_span.size_bytes());
stream::IntervalReader file_reader =
bundle_.GetTargetPayload(file_name_view);
if (const Status status =
backend_.ApplyTargetFile(file_name_view, file_reader);
!status.ok()) {
PW_LOG_ERROR("Failed to apply target file: %d",
static_cast<int>(status.code()));
return status;
}
}
return backend_.FinalizeApply();
}
Result<uint32_t> BundledUpdateManager::GetTransferId() {
if (!transfer_id_.has_value()) {
Result<uint32_t> possible_transfer_id =
backend_.EnableBundleTransferHandler();
PW_TRY(possible_transfer_id.status());
transfer_id_ = possible_transfer_id.value();
}
return transfer_id_.value();
}
pw::Status BundledUpdateManager::VerifyManifest() {
return pw::Status::Unimplemented();
}
pw::Status BundledUpdateManager::WriteManifest() {
return pw::Status::Unimplemented();
}
pw::Status BundledUpdateManager::BeforeUpdate() {
return backend_.BeforeUpdateStart();
}
void BundledUpdateManager::DisableTransferId() {
if (!transfer_id_.has_value()) {
return; // Nothing to do, already disabled.
}
backend_.DisableBundleTransferHandler();
}
pw::Status BundledUpdateManager::Abort() {
DisableTransferId();
PW_TRY(backend_.BeforeUpdateAbort());
if (bundle_open_) {
bundle_.Close();
}
return OkStatus();
}
pw::Status BundledUpdateManager::VerifyUpdate() {
DisableTransferId();
PW_TRY(backend_.BeforeBundleVerify());
ManifestAccessor manifest; // TODO(pwbug/456): Place-holder for now.
PW_TRY(bundle_.OpenAndVerify(manifest));
bundle_open_ = true;
return backend_.AfterBundleVerified();
}
} // namespace pw::software_update