1 //
2 // Copyright (C) 2023 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 #include <liblp/super_layout_builder.h>
17
18 #include <liblp/liblp.h>
19
20 #include "images.h"
21 #include "utility.h"
22 #include "writer.h"
23
24 using android::base::borrowed_fd;
25 using android::base::unique_fd;
26
27 namespace android {
28 namespace fs_mgr {
29
Open(borrowed_fd fd)30 bool SuperLayoutBuilder::Open(borrowed_fd fd) {
31 auto metadata = ReadFromImageFile(fd.get());
32 if (!metadata) {
33 return false;
34 }
35 return Open(*metadata.get());
36 }
37
Open(const void * data,size_t size)38 bool SuperLayoutBuilder::Open(const void* data, size_t size) {
39 auto metadata = ReadFromImageBlob(data, size);
40 if (!metadata) {
41 return false;
42 }
43 return Open(*metadata.get());
44 }
45
Open(const LpMetadata & metadata)46 bool SuperLayoutBuilder::Open(const LpMetadata& metadata) {
47 for (const auto& partition : metadata.partitions) {
48 if (partition.attributes & LP_PARTITION_ATTR_SLOT_SUFFIXED) {
49 // Retrofit devices are not supported.
50 return false;
51 }
52 if (!(partition.attributes & LP_PARTITION_ATTR_READONLY)) {
53 // Writable partitions are not supported.
54 return false;
55 }
56 }
57 if (!metadata.extents.empty()) {
58 // Partitions that already have extents are not supported (should
59 // never be true of super_empty.img).
60 return false;
61 }
62 if (metadata.block_devices.size() != 1) {
63 // Only one "super" is supported.
64 return false;
65 }
66
67 builder_ = MetadataBuilder::New(metadata);
68 return !!builder_;
69 }
70
AddPartition(const std::string & partition_name,const std::string & image_name,uint64_t partition_size)71 bool SuperLayoutBuilder::AddPartition(const std::string& partition_name,
72 const std::string& image_name, uint64_t partition_size) {
73 auto p = builder_->FindPartition(partition_name);
74 if (!p) {
75 return false;
76 }
77 if (!builder_->ResizePartition(p, partition_size)) {
78 return false;
79 }
80 image_map_.emplace(partition_name, image_name);
81 return true;
82 }
83
84 // Fill the space between each extent, if any, with either a fill or dontcare
85 // extent. The caller constructs a sample extent to re-use.
AddGapExtents(std::vector<SuperImageExtent> * extents,SuperImageExtent::Type gap_type)86 static bool AddGapExtents(std::vector<SuperImageExtent>* extents, SuperImageExtent::Type gap_type) {
87 std::vector<SuperImageExtent> old = std::move(*extents);
88 std::sort(old.begin(), old.end());
89
90 *extents = {};
91
92 uint64_t current_offset = 0;
93 for (const auto& extent : old) {
94 // Check for overlapping extents - this would be a serious error.
95 if (current_offset > extent.offset) {
96 LOG(INFO) << "Overlapping extents detected; cannot layout temporary super image";
97 return false;
98 }
99
100 if (extent.offset != current_offset) {
101 uint64_t gap_size = extent.offset - current_offset;
102 extents->emplace_back(current_offset, gap_size, gap_type);
103 current_offset = extent.offset;
104 }
105
106 extents->emplace_back(extent);
107 current_offset += extent.size;
108 }
109 return true;
110 }
111
GetImageLayout()112 std::vector<SuperImageExtent> SuperLayoutBuilder::GetImageLayout() {
113 auto metadata = builder_->Export();
114 if (!metadata) {
115 return {};
116 }
117
118 std::vector<SuperImageExtent> extents;
119
120 // Write the primary and backup copies of geometry.
121 std::string geometry_bytes = SerializeGeometry(metadata->geometry);
122 auto blob = std::make_shared<std::string>(std::move(geometry_bytes));
123
124 extents.emplace_back(0, GetPrimaryGeometryOffset(), SuperImageExtent::Type::ZERO);
125 extents.emplace_back(GetPrimaryGeometryOffset(), blob);
126 extents.emplace_back(GetBackupGeometryOffset(), blob);
127
128 // Write the primary and backup copies of each metadata slot. When flashing,
129 // all metadata copies are the same, even for different slots.
130 std::string metadata_bytes = SerializeMetadata(*metadata.get());
131
132 // Align metadata size to 4KB. This makes the layout easily compatible with
133 // libsparse.
134 static constexpr size_t kSparseAlignment = 4096;
135 size_t metadata_aligned_bytes;
136 if (!AlignTo(metadata_bytes.size(), kSparseAlignment, &metadata_aligned_bytes)) {
137 LOG(ERROR) << "Unable to align metadata size " << metadata_bytes.size() << " to "
138 << kSparseAlignment;
139 return {};
140 }
141 metadata_bytes.resize(metadata_aligned_bytes, '\0');
142
143 // However, alignment can cause larger-than-supported metadata blocks. Fall
144 // back to fastbootd/update-super.
145 if (metadata_bytes.size() > metadata->geometry.metadata_max_size) {
146 LOG(VERBOSE) << "Aligned metadata size " << metadata_bytes.size()
147 << " is larger than maximum metadata size "
148 << metadata->geometry.metadata_max_size;
149 return {};
150 }
151
152 blob = std::make_shared<std::string>(std::move(metadata_bytes));
153 for (uint32_t i = 0; i < metadata->geometry.metadata_slot_count; i++) {
154 int64_t metadata_primary = GetPrimaryMetadataOffset(metadata->geometry, i);
155 int64_t metadata_backup = GetBackupMetadataOffset(metadata->geometry, i);
156 extents.emplace_back(metadata_primary, blob);
157 extents.emplace_back(metadata_backup, blob);
158 }
159
160 // Add extents for each partition.
161 for (const auto& partition : metadata->partitions) {
162 auto partition_name = GetPartitionName(partition);
163 auto image_name_iter = image_map_.find(partition_name);
164 if (image_name_iter == image_map_.end()) {
165 if (partition.num_extents != 0) {
166 LOG(ERROR) << "Partition " << partition_name
167 << " has extents but no image filename";
168 return {};
169 }
170 continue;
171 }
172 const auto& image_name = image_name_iter->second;
173
174 uint64_t image_offset = 0;
175 for (uint32_t i = 0; i < partition.num_extents; i++) {
176 const auto& e = metadata->extents[partition.first_extent_index + i];
177
178 if (e.target_type != LP_TARGET_TYPE_LINEAR) {
179 // Any type other than LINEAR isn't understood here. We don't even
180 // bother with ZERO, which is never generated.
181 LOG(INFO) << "Unknown extent type from liblp: " << e.target_type;
182 return {};
183 }
184
185 size_t size = e.num_sectors * LP_SECTOR_SIZE;
186 uint64_t super_offset = e.target_data * LP_SECTOR_SIZE;
187 extents.emplace_back(super_offset, size, image_name, image_offset);
188
189 image_offset += size;
190 }
191 }
192
193 if (!AddGapExtents(&extents, SuperImageExtent::Type::DONTCARE)) {
194 return {};
195 }
196 return extents;
197 }
198
operator ==(const SuperImageExtent & other) const199 bool SuperImageExtent::operator==(const SuperImageExtent& other) const {
200 if (offset != other.offset) {
201 return false;
202 }
203 if (size != other.size) {
204 return false;
205 }
206 if (type != other.type) {
207 return false;
208 }
209 switch (type) {
210 case Type::DATA:
211 return *blob == *other.blob;
212 case Type::PARTITION:
213 return image_name == other.image_name && image_offset == other.image_offset;
214 default:
215 return true;
216 }
217 }
218
operator <<(std::ostream & stream,const SuperImageExtent & extent)219 std::ostream& operator<<(std::ostream& stream, const SuperImageExtent& extent) {
220 stream << "extent:" << extent.offset << ":" << extent.size << ":";
221 switch (extent.type) {
222 case SuperImageExtent::Type::DATA:
223 stream << "data";
224 break;
225 case SuperImageExtent::Type::PARTITION:
226 stream << "partition:" << extent.image_name << ":" << extent.image_offset;
227 break;
228 case SuperImageExtent::Type::ZERO:
229 stream << "zero";
230 break;
231 case SuperImageExtent::Type::DONTCARE:
232 stream << "dontcare";
233 break;
234 default:
235 stream << "invalid";
236 }
237 return stream;
238 }
239
240 } // namespace fs_mgr
241 } // namespace android
242