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 if (!manifest.has_dynamic_partition_metadata()) {
43 return;
44 }
45
46 // Key: partition name ("system"). Value: group name ("group").
47 // No suffix.
48 std::map<std::string_view, std::string_view> partition_group_map;
49 const auto& metadata_groups = manifest.dynamic_partition_metadata().groups();
50 groups_.reserve(metadata_groups.size());
51 for (const auto& group : metadata_groups) {
52 groups_.emplace_back(Group{group.name() + target_suffix_, &group});
53 for (const auto& partition_name : group.partition_names()) {
54 partition_group_map[partition_name] = group.name();
55 }
56 }
57
58 for (const auto& p : manifest.partitions()) {
59 auto it = partition_group_map.find(p.partition_name());
60 if (it != partition_group_map.end()) {
61 partitions_.emplace_back(Partition{p.partition_name() + target_suffix_,
62 std::string(it->second) + target_suffix_, &p});
63 }
64 }
65 }
66
ShrinkPartitions() const67 bool SnapshotMetadataUpdater::ShrinkPartitions() const {
68 for (const auto& partition_update : partitions_) {
69 auto* existing_partition = builder_->FindPartition(partition_update.name);
70 if (existing_partition == nullptr) {
71 continue;
72 }
73 auto new_size = partition_update->new_partition_info().size();
74 if (existing_partition->size() <= new_size) {
75 continue;
76 }
77 if (!builder_->ResizePartition(existing_partition, new_size)) {
78 return false;
79 }
80 }
81 return true;
82 }
83
DeletePartitions() const84 bool SnapshotMetadataUpdater::DeletePartitions() const {
85 std::vector<std::string> partitions_to_delete;
86 // Don't delete partitions in groups where the group name doesn't have target_suffix,
87 // e.g. default.
88 for (auto* existing_partition : ListPartitionsWithSuffix(builder_, target_suffix_)) {
89 auto iter = std::find_if(partitions_.begin(), partitions_.end(),
90 [existing_partition](auto&& partition_update) {
91 return partition_update.name == existing_partition->name();
92 });
93 // Update package metadata doesn't have this partition. Prepare to delete it.
94 // Not deleting from builder_ yet because it may break ListPartitionsWithSuffix if it were
95 // to return an iterable view of builder_.
96 if (iter == partitions_.end()) {
97 partitions_to_delete.push_back(existing_partition->name());
98 }
99 }
100
101 for (const auto& partition_name : partitions_to_delete) {
102 builder_->RemovePartition(partition_name);
103 }
104 return true;
105 }
106
MovePartitionsToDefault() const107 bool SnapshotMetadataUpdater::MovePartitionsToDefault() const {
108 for (const auto& partition_update : partitions_) {
109 auto* existing_partition = builder_->FindPartition(partition_update.name);
110 if (existing_partition == nullptr) {
111 continue;
112 }
113 if (existing_partition->group_name() == partition_update.group_name) {
114 continue;
115 }
116 // Move to "default" group (which doesn't have maximum size constraint)
117 // temporarily.
118 if (!builder_->ChangePartitionGroup(existing_partition, android::fs_mgr::kDefaultGroup)) {
119 return false;
120 }
121 }
122 return true;
123 }
124
ShrinkGroups() const125 bool SnapshotMetadataUpdater::ShrinkGroups() const {
126 for (const auto& group_update : groups_) {
127 auto* existing_group = builder_->FindGroup(group_update.name);
128 if (existing_group == nullptr) {
129 continue;
130 }
131 if (existing_group->maximum_size() <= group_update->size()) {
132 continue;
133 }
134 if (!builder_->ChangeGroupSize(existing_group->name(), group_update->size())) {
135 return false;
136 }
137 }
138 return true;
139 }
140
DeleteGroups() const141 bool SnapshotMetadataUpdater::DeleteGroups() const {
142 std::vector<std::string> existing_groups = builder_->ListGroups();
143 for (const auto& existing_group_name : existing_groups) {
144 // Don't delete groups without target suffix, e.g. default.
145 if (!android::base::EndsWith(existing_group_name, target_suffix_)) {
146 continue;
147 }
148
149 auto iter = std::find_if(groups_.begin(), groups_.end(),
150 [&existing_group_name](auto&& group_update) {
151 return group_update.name == existing_group_name;
152 });
153 // Update package metadata has this group as well, so not deleting it.
154 if (iter != groups_.end()) {
155 continue;
156 }
157 // Update package metadata doesn't have this group. Before deleting it, sanity check that it
158 // doesn't have any partitions left. Update metadata shouldn't assign any partitions to this
159 // group, so all partitions that originally belong to this group should be moved by
160 // MovePartitionsToDefault at this point.
161 auto existing_partitions_in_group = builder_->ListPartitionsInGroup(existing_group_name);
162 if (!existing_partitions_in_group.empty()) {
163 std::vector<std::string> partition_names_in_group;
164 std::transform(existing_partitions_in_group.begin(), existing_partitions_in_group.end(),
165 std::back_inserter(partition_names_in_group),
166 [](auto* p) { return p->name(); });
167 LOG(ERROR)
168 << "Group " << existing_group_name
169 << " cannot be deleted because the following partitions are left unassigned: ["
170 << android::base::Join(partition_names_in_group, ",") << "]";
171 return false;
172 }
173 builder_->RemoveGroupAndPartitions(existing_group_name);
174 }
175 return true;
176 }
177
AddGroups() const178 bool SnapshotMetadataUpdater::AddGroups() const {
179 for (const auto& group_update : groups_) {
180 if (builder_->FindGroup(group_update.name) == nullptr) {
181 if (!builder_->AddGroup(group_update.name, group_update->size())) {
182 return false;
183 }
184 }
185 }
186 return true;
187 }
188
GrowGroups() const189 bool SnapshotMetadataUpdater::GrowGroups() const {
190 for (const auto& group_update : groups_) {
191 auto* existing_group = builder_->FindGroup(group_update.name);
192 if (existing_group == nullptr) {
193 continue;
194 }
195 if (existing_group->maximum_size() >= group_update->size()) {
196 continue;
197 }
198 if (!builder_->ChangeGroupSize(existing_group->name(), group_update->size())) {
199 return false;
200 }
201 }
202 return true;
203 }
204
AddPartitions() const205 bool SnapshotMetadataUpdater::AddPartitions() const {
206 for (const auto& partition_update : partitions_) {
207 if (builder_->FindPartition(partition_update.name) == nullptr) {
208 auto* p =
209 builder_->AddPartition(partition_update.name, partition_update.group_name,
210 LP_PARTITION_ATTR_READONLY | LP_PARTITION_ATTR_UPDATED);
211 if (p == nullptr) {
212 return false;
213 }
214 }
215 }
216 // Will be resized in GrowPartitions.
217 return true;
218 }
219
GrowPartitions() const220 bool SnapshotMetadataUpdater::GrowPartitions() const {
221 for (const auto& partition_update : partitions_) {
222 auto* existing_partition = builder_->FindPartition(partition_update.name);
223 if (existing_partition == nullptr) {
224 continue;
225 }
226 auto new_size = partition_update->new_partition_info().size();
227 if (existing_partition->size() >= new_size) {
228 continue;
229 }
230 if (!builder_->ResizePartition(existing_partition, new_size)) {
231 return false;
232 }
233 }
234 return true;
235 }
236
MovePartitionsToCorrectGroup() const237 bool SnapshotMetadataUpdater::MovePartitionsToCorrectGroup() const {
238 for (const auto& partition_update : partitions_) {
239 auto* existing_partition = builder_->FindPartition(partition_update.name);
240 if (existing_partition == nullptr) {
241 continue;
242 }
243 if (existing_partition->group_name() == partition_update.group_name) {
244 continue;
245 }
246 if (!builder_->ChangePartitionGroup(existing_partition, partition_update.group_name)) {
247 return false;
248 }
249 }
250 return true;
251 }
252
Update() const253 bool SnapshotMetadataUpdater::Update() const {
254 // Remove extents used by COW devices by removing the COW group completely.
255 builder_->RemoveGroupAndPartitions(android::snapshot::kCowGroupName);
256
257 // The order of these operations are important so that we
258 // always have enough space to grow or add new partitions / groups.
259 // clang-format off
260 return ShrinkPartitions() &&
261 DeletePartitions() &&
262 MovePartitionsToDefault() &&
263 ShrinkGroups() &&
264 DeleteGroups() &&
265 AddGroups() &&
266 GrowGroups() &&
267 AddPartitions() &&
268 GrowPartitions() &&
269 MovePartitionsToCorrectGroup();
270 // clang-format on
271 }
272 } // namespace snapshot
273 } // namespace android
274