• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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