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