blob: 46c079f553e7349c0617206d6302ebaa5ba0abb9 [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/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<uint32_t>(
UpdateBundle::Fields::TARGETS_METADATA))[kTopLevelTargetsName]
.AsMessage(static_cast<uint32_t>(
SignedTargetsMetadata::Fields::SERIALIZED_TARGETS_METADATA));
protobuf::Bytes user_manifest =
bundle.AsStringToBytesMap(static_cast<uint32_t>(
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<uint32_t>(
Manifest::Fields::TARGETS_METADATA))[kTopLevelTargetsName];
protobuf::Bytes user_manifest =
manifest.AsBytes(static_cast<uint32_t>(Manifest::Fields::USER_MANIFEST));
return ManifestAccessor(targets_metadata, user_manifest);
}
protobuf::RepeatedMessages ManifestAccessor::GetTargetFiles() {
PW_TRY(status());
return targets_metadata_.AsRepeatedMessages(
static_cast<uint32_t>(TargetsMetadata::Fields::TARGET_FILES));
}
protobuf::Uint32 ManifestAccessor::GetVersion() {
PW_TRY(status());
return targets_metadata_
.AsMessage(
static_cast<uint32_t>(TargetsMetadata::Fields::COMMON_METADATA))
.AsUint32(static_cast<uint32_t>(CommonMetadata::Fields::VERSION));
}
Status ManifestAccessor::Export(stream::Writer& writer) {
PW_TRY(status());
// Write out the targets metadata map.
stream::MemoryReader name_reader(as_bytes(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<uint32_t>(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<uint32_t>(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<std::byte, MAX_TARGET_NAME_LENGTH> 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<ByteSpan> 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<const char*>(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<uint32_t>(TargetFile::Fields::FILE_NAME));
Result<bool> 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