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