// 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/manifest_accessor.h" #include "pw_software_update/config.h" #include "pw_software_update/update_bundle.pwpb.h" #include "pw_software_update/update_bundle_accessor.h" namespace pw::software_update { ManifestAccessor ManifestAccessor::FromBundle(protobuf::Message bundle) { protobuf::Message targets_metadata = bundle .AsStringToMessageMap(static_cast( UpdateBundle::Fields::TARGETS_METADATA))[kTopLevelTargetsName] .AsMessage(static_cast( SignedTargetsMetadata::Fields::SERIALIZED_TARGETS_METADATA)); protobuf::Bytes user_manifest = bundle.AsStringToBytesMap(static_cast( UpdateBundle::Fields::TARGET_PAYLOADS))[kUserManifestTargetFileName]; return ManifestAccessor(targets_metadata, user_manifest); } ManifestAccessor ManifestAccessor::FromManifest(protobuf::Message manifest) { protobuf::Message targets_metadata = manifest.AsStringToMessageMap(static_cast( Manifest::Fields::TARGETS_METADATA))[kTopLevelTargetsName]; protobuf::Bytes user_manifest = manifest.AsBytes(static_cast(Manifest::Fields::USER_MANIFEST)); return ManifestAccessor(targets_metadata, user_manifest); } protobuf::RepeatedMessages ManifestAccessor::GetTargetFiles() { PW_TRY(status()); return targets_metadata_.AsRepeatedMessages( static_cast(TargetsMetadata::Fields::TARGET_FILES)); } protobuf::Uint32 ManifestAccessor::GetVersion() { PW_TRY(status()); return targets_metadata_ .AsMessage( static_cast(TargetsMetadata::Fields::COMMON_METADATA)) .AsUint32(static_cast(CommonMetadata::Fields::VERSION)); } Status ManifestAccessor::Export(stream::Writer& writer) { PW_TRY(status()); // Write out the targets metadata map. stream::MemoryReader name_reader( std::as_bytes(std::span(kTopLevelTargetsName))); stream::IntervalReader metadata_reader = targets_metadata_.ToBytes().GetBytesReader(); std::byte stream_pipe_buffer[WRITE_MANIFEST_STREAM_PIPE_BUFFER_SIZE]; PW_TRY(protobuf::WriteProtoStringToBytesMapEntry( static_cast(Manifest::Fields::TARGETS_METADATA), name_reader, kTopLevelTargetsName.size(), metadata_reader, metadata_reader.interval_size(), stream_pipe_buffer, writer)); // The user manifest is optional, write it out if available(). stream::IntervalReader user_manifest_reader = user_manifest_.GetBytesReader(); if (user_manifest_reader.ok()) { protobuf::StreamEncoder encoder(writer, {}); PW_TRY(encoder.WriteBytesFromStream( static_cast(Manifest::Fields::USER_MANIFEST), user_manifest_reader, user_manifest_reader.interval_size(), stream_pipe_buffer)); } return OkStatus(); } protobuf::Message ManifestAccessor::GetTargetFile(protobuf::String name) { PW_TRY(status()); std::array name_buf = {}; stream::IntervalReader name_reader = name.GetBytesReader(); PW_TRY(name_reader.status()); if (name_reader.interval_size() > name_buf.size()) { return Status::OutOfRange(); } Result read_result = name_reader.Read(name_buf); PW_TRY(read_result.status()); const ConstByteSpan name_span = read_result.value(); const std::string_view name_view( reinterpret_cast(name_span.data()), name_span.size_bytes()); return GetTargetFile(name_view); } protobuf::Message ManifestAccessor::GetTargetFile(std::string_view name) { PW_TRY(status()); for (protobuf::Message target_file : GetTargetFiles()) { protobuf::String target_name = target_file.AsString( static_cast(TargetFile::Fields::FILE_NAME)); Result compare_result = target_name.Equal(name); PW_TRY(compare_result.status()); if (compare_result.value()) { return target_file; } } return Status::NotFound(); } } // namespace pw::software_update