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::kTargetsMetadata))[kTopLevelTargetsName]
27 .AsMessage(static_cast<uint32_t>(
28 SignedTargetsMetadata::Fields::kSerializedTargetsMetadata));
29
30 protobuf::Bytes user_manifest =
31 bundle.AsStringToBytesMap(static_cast<uint32_t>(
32 UpdateBundle::Fields::kTargetPayloads))[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::kTargetsMetadata))[kTopLevelTargetsName];
41
42 protobuf::Bytes user_manifest =
43 manifest.AsBytes(static_cast<uint32_t>(Manifest::Fields::kUserManifest));
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::kTargetFiles));
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::kCommonMetadata))
59 .AsUint32(static_cast<uint32_t>(CommonMetadata::Fields::kVersion));
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(as_bytes(span(kTopLevelTargetsName)));
67 stream::IntervalReader metadata_reader =
68 targets_metadata_.ToBytes().GetBytesReader();
69 std::byte stream_pipe_buffer[WRITE_MANIFEST_STREAM_PIPE_BUFFER_SIZE];
70 PW_TRY(protobuf::WriteProtoStringToBytesMapEntry(
71 static_cast<uint32_t>(Manifest::Fields::kTargetsMetadata),
72 name_reader,
73 kTopLevelTargetsName.size(),
74 metadata_reader,
75 metadata_reader.interval_size(),
76 stream_pipe_buffer,
77 writer));
78
79 // The user manifest is optional, write it out if available().
80 stream::IntervalReader user_manifest_reader = user_manifest_.GetBytesReader();
81 if (user_manifest_reader.ok()) {
82 protobuf::StreamEncoder encoder(writer, {});
83 PW_TRY(encoder.WriteBytesFromStream(
84 static_cast<uint32_t>(Manifest::Fields::kUserManifest),
85 user_manifest_reader,
86 user_manifest_reader.interval_size(),
87 stream_pipe_buffer));
88 }
89
90 return OkStatus();
91 }
92
GetTargetFile(protobuf::String name)93 protobuf::Message ManifestAccessor::GetTargetFile(protobuf::String name) {
94 PW_TRY(status());
95
96 std::array<std::byte, MAX_TARGET_NAME_LENGTH> name_buf = {};
97
98 stream::IntervalReader name_reader = name.GetBytesReader();
99 PW_TRY(name_reader.status());
100
101 if (name_reader.interval_size() > name_buf.size()) {
102 return Status::OutOfRange();
103 }
104
105 Result<ByteSpan> read_result = name_reader.Read(name_buf);
106 PW_TRY(read_result.status());
107
108 const ConstByteSpan name_span = read_result.value();
109 const std::string_view name_view(
110 reinterpret_cast<const char*>(name_span.data()), name_span.size_bytes());
111
112 return GetTargetFile(name_view);
113 }
114
GetTargetFile(std::string_view name)115 protobuf::Message ManifestAccessor::GetTargetFile(std::string_view name) {
116 PW_TRY(status());
117
118 for (protobuf::Message target_file : GetTargetFiles()) {
119 protobuf::String target_name = target_file.AsString(
120 static_cast<uint32_t>(TargetFile::Fields::kFileName));
121 Result<bool> compare_result = target_name.Equal(name);
122 PW_TRY(compare_result.status());
123 if (compare_result.value()) {
124 return target_file;
125 }
126 }
127
128 return Status::NotFound();
129 }
130
131 } // namespace pw::software_update
132