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