1 //
2 // Copyright (C) 2019 The Android Open Source Project
3 //
4 // Licensed under the Apache License, Version 2.0 (the "License");
5 // you may not use this file except in compliance with the License.
6 // You may obtain a copy of the License at
7 //
8 // http://www.apache.org/licenses/LICENSE-2.0
9 //
10 // Unless required by applicable law or agreed to in writing, software
11 // distributed under the License is distributed on an "AS IS" BASIS,
12 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 // See the License for the specific language governing permissions and
14 // limitations under the License.
15 //
16
17 #include "snapshot_metadata_updater.h"
18
19 #include <algorithm>
20 #include <map>
21 #include <optional>
22 #include <set>
23 #include <string>
24 #include <string_view>
25 #include <vector>
26
27 #include <android-base/logging.h>
28 #include <android-base/strings.h>
29 #include <fs_mgr.h>
30 #include <libsnapshot/snapshot.h>
31
32 using android::fs_mgr::MetadataBuilder;
33 using android::fs_mgr::Partition;
34 using android::fs_mgr::SlotSuffixForSlotNumber;
35 using chromeos_update_engine::DeltaArchiveManifest;
36
37 namespace android {
38 namespace snapshot {
SnapshotMetadataUpdater(MetadataBuilder * builder,uint32_t target_slot,const DeltaArchiveManifest & manifest)39 SnapshotMetadataUpdater::SnapshotMetadataUpdater(MetadataBuilder* builder, uint32_t target_slot,
40 const DeltaArchiveManifest& manifest)
41 : builder_(builder), target_suffix_(SlotSuffixForSlotNumber(target_slot)) {
42 partial_update_ = manifest.partial_update();
43
44 if (!manifest.has_dynamic_partition_metadata()) {
45 return;
46 }
47
48 // Key: partition name ("system"). Value: group name ("group").
49 // No suffix.
50 std::map<std::string_view, std::string_view> partition_group_map;
51 const auto& metadata_groups = manifest.dynamic_partition_metadata().groups();
52 groups_.reserve(metadata_groups.size());
53 for (const auto& group : metadata_groups) {
54 groups_.emplace_back(Group{group.name() + target_suffix_, &group});
55 for (const auto& partition_name : group.partition_names()) {
56 partition_group_map[partition_name] = group.name();
57 }
58 }
59
60 for (const auto& p : manifest.partitions()) {
61 auto it = partition_group_map.find(p.partition_name());
62 if (it != partition_group_map.end()) {
63 partitions_.emplace_back(Partition{p.partition_name() + target_suffix_,
64 std::string(it->second) + target_suffix_, &p});
65 }
66 }
67
68 }
69
ShrinkPartitions() const70 bool SnapshotMetadataUpdater::ShrinkPartitions() const {
71 for (const auto& partition_update : partitions_) {
72 auto* existing_partition = builder_->FindPartition(partition_update.name);
73 if (existing_partition == nullptr) {
74 continue;
75 }
76 auto new_size = partition_update->new_partition_info().size();
77 if (existing_partition->size() <= new_size) {
78 continue;
79 }
80 if (!builder_->ResizePartition(existing_partition, new_size)) {
81 return false;
82 }
83 }
84 return true;
85 }
86
DeletePartitions() const87 bool SnapshotMetadataUpdater::DeletePartitions() const {
88 // For partial update, not all dynamic partitions are included in the payload.
89 // TODO(xunchang) delete the untouched partitions whose group is in the payload.
90 // e.g. Delete vendor in the following scenario
91 // On device:
92 // Group A: system, vendor
93 // In payload:
94 // Group A: system
95 if (partial_update_) {
96 LOG(INFO) << "Skip deleting partitions for partial update";
97 return true;
98 }
99
100 std::vector<std::string> partitions_to_delete;
101 // Don't delete partitions in groups where the group name doesn't have target_suffix,
102 // e.g. default.
103 for (auto* existing_partition : ListPartitionsWithSuffix(builder_, target_suffix_)) {
104 auto iter = std::find_if(partitions_.begin(), partitions_.end(),
105 [existing_partition](auto&& partition_update) {
106 return partition_update.name == existing_partition->name();
107 });
108 // Update package metadata doesn't have this partition. Prepare to delete it.
109 // Not deleting from builder_ yet because it may break ListPartitionsWithSuffix if it were
110 // to return an iterable view of builder_.
111 if (iter == partitions_.end()) {
112 partitions_to_delete.push_back(existing_partition->name());
113 }
114 }
115
116 for (const auto& partition_name : partitions_to_delete) {
117 builder_->RemovePartition(partition_name);
118 }
119 return true;
120 }
121
MovePartitionsToDefault() const122 bool SnapshotMetadataUpdater::MovePartitionsToDefault() const {
123 for (const auto& partition_update : partitions_) {
124 auto* existing_partition = builder_->FindPartition(partition_update.name);
125 if (existing_partition == nullptr) {
126 continue;
127 }
128 if (existing_partition->group_name() == partition_update.group_name) {
129 continue;
130 }
131 // Move to "default" group (which doesn't have maximum size constraint)
132 // temporarily.
133 if (!builder_->ChangePartitionGroup(existing_partition, android::fs_mgr::kDefaultGroup)) {
134 return false;
135 }
136 }
137 return true;
138 }
139
ShrinkGroups() const140 bool SnapshotMetadataUpdater::ShrinkGroups() const {
141 for (const auto& group_update : groups_) {
142 auto* existing_group = builder_->FindGroup(group_update.name);
143 if (existing_group == nullptr) {
144 continue;
145 }
146 if (existing_group->maximum_size() <= group_update->size()) {
147 continue;
148 }
149 if (!builder_->ChangeGroupSize(existing_group->name(), group_update->size())) {
150 return false;
151 }
152 }
153 return true;
154 }
155
DeleteGroups() const156 bool SnapshotMetadataUpdater::DeleteGroups() const {
157 if (partial_update_) {
158 LOG(INFO) << "Skip deleting groups for partial update";
159 return true;
160 }
161
162 std::vector<std::string> existing_groups = builder_->ListGroups();
163 for (const auto& existing_group_name : existing_groups) {
164 // Don't delete groups without target suffix, e.g. default.
165 if (!android::base::EndsWith(existing_group_name, target_suffix_)) {
166 continue;
167 }
168
169 auto iter = std::find_if(groups_.begin(), groups_.end(),
170 [&existing_group_name](auto&& group_update) {
171 return group_update.name == existing_group_name;
172 });
173 // Update package metadata has this group as well, so not deleting it.
174 if (iter != groups_.end()) {
175 continue;
176 }
177 // Update package metadata doesn't have this group. Before deleting it, check that it
178 // doesn't have any partitions left. Update metadata shouldn't assign any partitions to
179 // this group, so all partitions that originally belong to this group should be moved by
180 // MovePartitionsToDefault at this point.
181 auto existing_partitions_in_group = builder_->ListPartitionsInGroup(existing_group_name);
182 if (!existing_partitions_in_group.empty()) {
183 std::vector<std::string> partition_names_in_group;
184 std::transform(existing_partitions_in_group.begin(), existing_partitions_in_group.end(),
185 std::back_inserter(partition_names_in_group),
186 [](auto* p) { return p->name(); });
187 LOG(ERROR)
188 << "Group " << existing_group_name
189 << " cannot be deleted because the following partitions are left unassigned: ["
190 << android::base::Join(partition_names_in_group, ",") << "]";
191 return false;
192 }
193 builder_->RemoveGroupAndPartitions(existing_group_name);
194 }
195 return true;
196 }
197
AddGroups() const198 bool SnapshotMetadataUpdater::AddGroups() const {
199 for (const auto& group_update : groups_) {
200 if (builder_->FindGroup(group_update.name) == nullptr) {
201 if (!builder_->AddGroup(group_update.name, group_update->size())) {
202 return false;
203 }
204 }
205 }
206 return true;
207 }
208
GrowGroups() const209 bool SnapshotMetadataUpdater::GrowGroups() const {
210 for (const auto& group_update : groups_) {
211 auto* existing_group = builder_->FindGroup(group_update.name);
212 if (existing_group == nullptr) {
213 continue;
214 }
215 if (existing_group->maximum_size() >= group_update->size()) {
216 continue;
217 }
218 if (!builder_->ChangeGroupSize(existing_group->name(), group_update->size())) {
219 return false;
220 }
221 }
222 return true;
223 }
224
AddPartitions() const225 bool SnapshotMetadataUpdater::AddPartitions() const {
226 for (const auto& partition_update : partitions_) {
227 if (builder_->FindPartition(partition_update.name) == nullptr) {
228 auto* p =
229 builder_->AddPartition(partition_update.name, partition_update.group_name,
230 LP_PARTITION_ATTR_READONLY | LP_PARTITION_ATTR_UPDATED);
231 if (p == nullptr) {
232 return false;
233 }
234 }
235 }
236 // Will be resized in GrowPartitions.
237 return true;
238 }
239
GrowPartitions() const240 bool SnapshotMetadataUpdater::GrowPartitions() const {
241 for (const auto& partition_update : partitions_) {
242 auto* existing_partition = builder_->FindPartition(partition_update.name);
243 if (existing_partition == nullptr) {
244 continue;
245 }
246 auto new_size = partition_update->new_partition_info().size();
247 if (existing_partition->size() >= new_size) {
248 continue;
249 }
250 if (!builder_->ResizePartition(existing_partition, new_size)) {
251 return false;
252 }
253 }
254 return true;
255 }
256
MovePartitionsToCorrectGroup() const257 bool SnapshotMetadataUpdater::MovePartitionsToCorrectGroup() const {
258 for (const auto& partition_update : partitions_) {
259 auto* existing_partition = builder_->FindPartition(partition_update.name);
260 if (existing_partition == nullptr) {
261 continue;
262 }
263 if (existing_partition->group_name() == partition_update.group_name) {
264 continue;
265 }
266 if (!builder_->ChangePartitionGroup(existing_partition, partition_update.group_name)) {
267 return false;
268 }
269 }
270 return true;
271 }
272
Update() const273 bool SnapshotMetadataUpdater::Update() const {
274 // Remove extents used by COW devices by removing the COW group completely.
275 builder_->RemoveGroupAndPartitions(android::snapshot::kCowGroupName);
276
277 // The order of these operations are important so that we
278 // always have enough space to grow or add new partitions / groups.
279 // clang-format off
280 return ShrinkPartitions() &&
281 DeletePartitions() &&
282 MovePartitionsToDefault() &&
283 ShrinkGroups() &&
284 DeleteGroups() &&
285 AddGroups() &&
286 GrowGroups() &&
287 AddPartitions() &&
288 GrowPartitions() &&
289 MovePartitionsToCorrectGroup();
290 // clang-format on
291 }
292 } // namespace snapshot
293 } // namespace android
294