1 // Copyright 2021 The Pigweed Authors
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License"); you may not
4 // use this file except in compliance with the License. You may obtain a copy of
5 // the License at
6 //
7 // https://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
11 // WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
12 // License for the specific language governing permissions and limitations under
13 // the License.
14 #include "pw_software_update/manifest_accessor.h"
15
16 #include "pw_software_update/config.h"
17 #include "pw_software_update/update_bundle.pwpb.h"
18 #include "pw_software_update/update_bundle_accessor.h"
19
20 namespace pw::software_update {
21
FromBundle(protobuf::Message bundle)22 ManifestAccessor ManifestAccessor::FromBundle(protobuf::Message bundle) {
23 protobuf::Message targets_metadata =
24 bundle
25 .AsStringToMessageMap(static_cast<uint32_t>(
26 UpdateBundle::Fields::TARGETS_METADATA))[kTopLevelTargetsName]
27 .AsMessage(static_cast<uint32_t>(
28 SignedTargetsMetadata::Fields::SERIALIZED_TARGETS_METADATA));
29
30 protobuf::Bytes user_manifest =
31 bundle.AsStringToBytesMap(static_cast<uint32_t>(
32 UpdateBundle::Fields::TARGET_PAYLOADS))[kUserManifestTargetFileName];
33
34 return ManifestAccessor(targets_metadata, user_manifest);
35 }
36
FromManifest(protobuf::Message manifest)37 ManifestAccessor ManifestAccessor::FromManifest(protobuf::Message manifest) {
38 protobuf::Message targets_metadata =
39 manifest.AsStringToMessageMap(static_cast<uint32_t>(
40 Manifest::Fields::TARGETS_METADATA))[kTopLevelTargetsName];
41
42 protobuf::Bytes user_manifest =
43 manifest.AsBytes(static_cast<uint32_t>(Manifest::Fields::USER_MANIFEST));
44
45 return ManifestAccessor(targets_metadata, user_manifest);
46 }
47
GetTargetFiles()48 protobuf::RepeatedMessages ManifestAccessor::GetTargetFiles() {
49 PW_TRY(status());
50 return targets_metadata_.AsRepeatedMessages(
51 static_cast<uint32_t>(TargetsMetadata::Fields::TARGET_FILES));
52 }
53
GetVersion()54 protobuf::Uint32 ManifestAccessor::GetVersion() {
55 PW_TRY(status());
56 return targets_metadata_
57 .AsMessage(
58 static_cast<uint32_t>(TargetsMetadata::Fields::COMMON_METADATA))
59 .AsUint32(static_cast<uint32_t>(CommonMetadata::Fields::VERSION));
60 }
61
Export(stream::Writer & writer)62 Status ManifestAccessor::Export(stream::Writer& writer) {
63 PW_TRY(status());
64
65 // Write out the targets metadata map.
66 stream::MemoryReader name_reader(
67 std::as_bytes(std::span(kTopLevelTargetsName)));
68 stream::IntervalReader metadata_reader =
69 targets_metadata_.ToBytes().GetBytesReader();
70 std::byte stream_pipe_buffer[WRITE_MANIFEST_STREAM_PIPE_BUFFER_SIZE];
71 PW_TRY(protobuf::WriteProtoStringToBytesMapEntry(
72 static_cast<uint32_t>(Manifest::Fields::TARGETS_METADATA),
73 name_reader,
74 kTopLevelTargetsName.size(),
75 metadata_reader,
76 metadata_reader.interval_size(),
77 stream_pipe_buffer,
78 writer));
79
80 // The user manifest is optional, write it out if available().
81 stream::IntervalReader user_manifest_reader = user_manifest_.GetBytesReader();
82 if (user_manifest_reader.ok()) {
83 protobuf::StreamEncoder encoder(writer, {});
84 PW_TRY(encoder.WriteBytesFromStream(
85 static_cast<uint32_t>(Manifest::Fields::USER_MANIFEST),
86 user_manifest_reader,
87 user_manifest_reader.interval_size(),
88 stream_pipe_buffer));
89 }
90
91 return OkStatus();
92 }
93
GetTargetFile(protobuf::String name)94 protobuf::Message ManifestAccessor::GetTargetFile(protobuf::String name) {
95 PW_TRY(status());
96
97 std::array<std::byte, MAX_TARGET_NAME_LENGTH> name_buf = {};
98
99 stream::IntervalReader name_reader = name.GetBytesReader();
100 PW_TRY(name_reader.status());
101
102 if (name_reader.interval_size() > name_buf.size()) {
103 return Status::OutOfRange();
104 }
105
106 Result<ByteSpan> read_result = name_reader.Read(name_buf);
107 PW_TRY(read_result.status());
108
109 const ConstByteSpan name_span = read_result.value();
110 const std::string_view name_view(
111 reinterpret_cast<const char*>(name_span.data()), name_span.size_bytes());
112
113 return GetTargetFile(name_view);
114 }
115
GetTargetFile(std::string_view name)116 protobuf::Message ManifestAccessor::GetTargetFile(std::string_view name) {
117 PW_TRY(status());
118
119 for (protobuf::Message target_file : GetTargetFiles()) {
120 protobuf::String target_name = target_file.AsString(
121 static_cast<uint32_t>(TargetFile::Fields::FILE_NAME));
122 Result<bool> compare_result = target_name.Equal(name);
123 PW_TRY(compare_result.status());
124 if (compare_result.value()) {
125 return target_file;
126 }
127 }
128
129 return Status::NotFound();
130 }
131
132 } // namespace pw::software_update
133