1 /*
2 * Copyright (C) 2007 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 "writer.h"
18
19 #include <inttypes.h>
20 #include <string.h>
21 #include <unistd.h>
22
23 #include <string>
24
25 #include <android-base/file.h>
26 #include <android-base/unique_fd.h>
27
28 #include "reader.h"
29 #include "utility.h"
30
31 namespace android {
32 namespace fs_mgr {
33
SerializeGeometry(const LpMetadataGeometry & input)34 std::string SerializeGeometry(const LpMetadataGeometry& input) {
35 LpMetadataGeometry geometry = input;
36 memset(geometry.checksum, 0, sizeof(geometry.checksum));
37 SHA256(&geometry, sizeof(geometry), geometry.checksum);
38
39 std::string blob(reinterpret_cast<const char*>(&geometry), sizeof(geometry));
40 blob.resize(LP_METADATA_GEOMETRY_SIZE);
41 return blob;
42 }
43
CompareGeometry(const LpMetadataGeometry & g1,const LpMetadataGeometry & g2)44 static bool CompareGeometry(const LpMetadataGeometry& g1, const LpMetadataGeometry& g2) {
45 return g1.metadata_max_size == g2.metadata_max_size &&
46 g1.metadata_slot_count == g2.metadata_slot_count &&
47 g1.logical_block_size == g2.logical_block_size;
48 }
49
SerializeMetadata(const LpMetadata & input)50 std::string SerializeMetadata(const LpMetadata& input) {
51 LpMetadata metadata = input;
52 LpMetadataHeader& header = metadata.header;
53
54 // Serialize individual tables.
55 std::string partitions(reinterpret_cast<const char*>(metadata.partitions.data()),
56 metadata.partitions.size() * sizeof(LpMetadataPartition));
57 std::string extents(reinterpret_cast<const char*>(metadata.extents.data()),
58 metadata.extents.size() * sizeof(LpMetadataExtent));
59 std::string groups(reinterpret_cast<const char*>(metadata.groups.data()),
60 metadata.groups.size() * sizeof(LpMetadataPartitionGroup));
61 std::string block_devices(reinterpret_cast<const char*>(metadata.block_devices.data()),
62 metadata.block_devices.size() * sizeof(LpMetadataBlockDevice));
63
64 // Compute positions of tables.
65 header.partitions.offset = 0;
66 header.extents.offset = header.partitions.offset + partitions.size();
67 header.groups.offset = header.extents.offset + extents.size();
68 header.block_devices.offset = header.groups.offset + groups.size();
69 header.tables_size = header.block_devices.offset + block_devices.size();
70
71 // Compute payload checksum.
72 std::string tables = partitions + extents + groups + block_devices;
73 SHA256(tables.data(), tables.size(), header.tables_checksum);
74
75 // Compute header checksum.
76 memset(header.header_checksum, 0, sizeof(header.header_checksum));
77 SHA256(&header, header.header_size, header.header_checksum);
78
79 std::string header_blob =
80 std::string(reinterpret_cast<const char*>(&header), header.header_size);
81 return header_blob + tables;
82 }
83
84 // Perform checks so we don't accidentally overwrite valid metadata with
85 // potentially invalid metadata, or random partition data with metadata.
ValidateAndSerializeMetadata(const IPartitionOpener & opener,const LpMetadata & metadata,const std::string & slot_suffix,std::string * blob)86 static bool ValidateAndSerializeMetadata([[maybe_unused]] const IPartitionOpener& opener,
87 const LpMetadata& metadata, const std::string& slot_suffix,
88 std::string* blob) {
89 const LpMetadataGeometry& geometry = metadata.geometry;
90
91 *blob = SerializeMetadata(metadata);
92
93 // Make sure we're writing within the space reserved.
94 if (blob->size() > geometry.metadata_max_size) {
95 LERROR << "Logical partition metadata is too large. " << blob->size() << " > "
96 << geometry.metadata_max_size;
97 return false;
98 }
99
100 // Make sure the device has enough space to store two backup copies of the
101 // metadata.
102 uint64_t reserved_size = LP_METADATA_GEOMETRY_SIZE +
103 uint64_t(geometry.metadata_max_size) * geometry.metadata_slot_count;
104 uint64_t total_reserved = LP_PARTITION_RESERVED_BYTES + reserved_size * 2;
105
106 const LpMetadataBlockDevice* super_device = GetMetadataSuperBlockDevice(metadata);
107 if (!super_device) {
108 LERROR << "Logical partition metadata does not have a super block device.";
109 return false;
110 }
111
112 if (total_reserved > super_device->first_logical_sector * LP_SECTOR_SIZE) {
113 LERROR << "Not enough space to store all logical partition metadata slots.";
114 return false;
115 }
116 for (const auto& block_device : metadata.block_devices) {
117 std::string partition_name = GetBlockDevicePartitionName(block_device);
118 if (block_device.flags & LP_BLOCK_DEVICE_SLOT_SUFFIXED) {
119 if (slot_suffix.empty()) {
120 LERROR << "Block device " << partition_name << " requires a slot suffix,"
121 << " which could not be derived from the super partition name.";
122 return false;
123 }
124 partition_name += slot_suffix;
125 }
126
127 if ((block_device.first_logical_sector + 1) * LP_SECTOR_SIZE > block_device.size) {
128 LERROR << "Block device " << partition_name << " has invalid first sector "
129 << block_device.first_logical_sector << " for size " << block_device.size;
130 return false;
131 }
132
133 // When flashing on the device, check partition sizes. Don't do this on
134 // the host since there is no way to verify.
135 #if defined(__ANDROID__)
136 BlockDeviceInfo info;
137 if (!opener.GetInfo(partition_name, &info)) {
138 PERROR << partition_name << ": ioctl";
139 return false;
140 }
141 if (info.size != block_device.size) {
142 LERROR << "Block device " << partition_name << " size mismatch (expected"
143 << block_device.size << ", got " << info.size << ")";
144 return false;
145 }
146 #endif
147 }
148
149 // Make sure all partition entries reference valid extents.
150 for (const auto& partition : metadata.partitions) {
151 if (partition.first_extent_index + partition.num_extents > metadata.extents.size()) {
152 LERROR << "Partition references invalid extent.";
153 return false;
154 }
155 }
156
157 // Make sure all linear extents have a valid range.
158 uint64_t last_sector = super_device->size / LP_SECTOR_SIZE;
159 for (const auto& extent : metadata.extents) {
160 if (extent.target_type == LP_TARGET_TYPE_LINEAR) {
161 uint64_t physical_sector = extent.target_data;
162 if (physical_sector < super_device->first_logical_sector ||
163 physical_sector + extent.num_sectors > last_sector) {
164 LERROR << "Extent table entry is out of bounds.";
165 return false;
166 }
167 }
168 }
169 return true;
170 }
171
172 // Check that the given region is within metadata bounds.
ValidateMetadataRegion(const LpMetadata & metadata,uint64_t start,size_t size)173 static bool ValidateMetadataRegion(const LpMetadata& metadata, uint64_t start, size_t size) {
174 const LpMetadataBlockDevice* super_device = GetMetadataSuperBlockDevice(metadata);
175 if (!super_device) {
176 LERROR << __PRETTY_FUNCTION__ << " could not locate super block device in metadata";
177 return false;
178 }
179 if (start + size >= super_device->first_logical_sector * LP_SECTOR_SIZE) {
180 LERROR << __PRETTY_FUNCTION__ << " write of " << size << " bytes at " << start
181 << " overlaps with logical partition contents";
182 return false;
183 }
184 return true;
185 }
186
WritePrimaryMetadata(int fd,const LpMetadata & metadata,uint32_t slot_number,const std::string & blob,const std::function<bool (int,const std::string &)> & writer)187 static bool WritePrimaryMetadata(int fd, const LpMetadata& metadata, uint32_t slot_number,
188 const std::string& blob,
189 const std::function<bool(int, const std::string&)>& writer) {
190 int64_t primary_offset = GetPrimaryMetadataOffset(metadata.geometry, slot_number);
191 if (!ValidateMetadataRegion(metadata, primary_offset, blob.size())) {
192 return false;
193 }
194 if (SeekFile64(fd, primary_offset, SEEK_SET) < 0) {
195 PERROR << __PRETTY_FUNCTION__ << " lseek failed: offset " << primary_offset;
196 return false;
197 }
198 if (!writer(fd, blob)) {
199 PERROR << __PRETTY_FUNCTION__ << " write " << blob.size() << " bytes failed";
200 return false;
201 }
202 return true;
203 }
204
WriteBackupMetadata(int fd,const LpMetadata & metadata,uint32_t slot_number,const std::string & blob,const std::function<bool (int,const std::string &)> & writer)205 static bool WriteBackupMetadata(int fd, const LpMetadata& metadata, uint32_t slot_number,
206 const std::string& blob,
207 const std::function<bool(int, const std::string&)>& writer) {
208 int64_t backup_offset = GetBackupMetadataOffset(metadata.geometry, slot_number);
209 if (!ValidateMetadataRegion(metadata, backup_offset, blob.size())) {
210 return false;
211 }
212 if (SeekFile64(fd, backup_offset, SEEK_SET) < 0) {
213 PERROR << __PRETTY_FUNCTION__ << " lseek failed: offset " << backup_offset;
214 return false;
215 }
216 if (!writer(fd, blob)) {
217 PERROR << __PRETTY_FUNCTION__ << " backup write " << blob.size() << " bytes failed";
218 return false;
219 }
220 return true;
221 }
222
WriteMetadata(int fd,const LpMetadata & metadata,uint32_t slot_number,const std::string & blob,const std::function<bool (int,const std::string &)> & writer)223 static bool WriteMetadata(int fd, const LpMetadata& metadata, uint32_t slot_number,
224 const std::string& blob,
225 const std::function<bool(int, const std::string&)>& writer) {
226 // Make sure we're writing to a valid metadata slot.
227 if (slot_number >= metadata.geometry.metadata_slot_count) {
228 LERROR << "Invalid logical partition metadata slot number.";
229 return false;
230 }
231 if (!WritePrimaryMetadata(fd, metadata, slot_number, blob, writer)) {
232 return false;
233 }
234 if (!WriteBackupMetadata(fd, metadata, slot_number, blob, writer)) {
235 return false;
236 }
237 return true;
238 }
239
DefaultWriter(int fd,const std::string & blob)240 static bool DefaultWriter(int fd, const std::string& blob) {
241 return android::base::WriteFully(fd, blob.data(), blob.size());
242 }
243
244 #if defined(_WIN32)
245 static const int O_SYNC = 0;
246 #endif
247
FlashPartitionTable(const IPartitionOpener & opener,const std::string & super_partition,const LpMetadata & metadata)248 bool FlashPartitionTable(const IPartitionOpener& opener, const std::string& super_partition,
249 const LpMetadata& metadata) {
250 android::base::unique_fd fd = opener.Open(super_partition, O_RDWR | O_SYNC);
251 if (fd < 0) {
252 PERROR << __PRETTY_FUNCTION__ << " open failed: " << super_partition;
253 return false;
254 }
255
256 // This is only used in update_engine and fastbootd, where the super
257 // partition should be specified as a name (or by-name link), and
258 // therefore, we should be able to extract a slot suffix.
259 std::string slot_suffix = GetPartitionSlotSuffix(super_partition);
260
261 // Before writing geometry and/or logical partition tables, perform some
262 // basic checks that the geometry and tables are coherent, and will fit
263 // on the given block device.
264 std::string metadata_blob;
265 if (!ValidateAndSerializeMetadata(opener, metadata, slot_suffix, &metadata_blob)) {
266 return false;
267 }
268
269 // On retrofit devices, super_partition is system_other and might be set to readonly by
270 // fs_mgr_set_blk_ro(). Unset readonly so that fd can be written to.
271 if (!SetBlockReadonly(fd.get(), false)) {
272 PWARNING << __PRETTY_FUNCTION__ << " BLKROSET 0 failed: " << super_partition;
273 }
274
275 // Write zeroes to the first block.
276 std::string zeroes(LP_PARTITION_RESERVED_BYTES, 0);
277 if (SeekFile64(fd, 0, SEEK_SET) < 0) {
278 PERROR << __PRETTY_FUNCTION__ << " lseek failed: offset 0";
279 return false;
280 }
281 if (!android::base::WriteFully(fd, zeroes.data(), zeroes.size())) {
282 PERROR << __PRETTY_FUNCTION__ << " write " << zeroes.size() << " bytes failed";
283 return false;
284 }
285
286 LWARN << "Flashing new logical partition geometry to " << super_partition;
287
288 // Write geometry to the primary and backup locations.
289 std::string blob = SerializeGeometry(metadata.geometry);
290 if (SeekFile64(fd, GetPrimaryGeometryOffset(), SEEK_SET) < 0) {
291 PERROR << __PRETTY_FUNCTION__ << " lseek failed: primary geometry";
292 return false;
293 }
294 if (!android::base::WriteFully(fd, blob.data(), blob.size())) {
295 PERROR << __PRETTY_FUNCTION__ << " write " << blob.size() << " bytes failed";
296 return false;
297 }
298 if (SeekFile64(fd, GetBackupGeometryOffset(), SEEK_SET) < 0) {
299 PERROR << __PRETTY_FUNCTION__ << " lseek failed: backup geometry";
300 return false;
301 }
302 if (!android::base::WriteFully(fd, blob.data(), blob.size())) {
303 PERROR << __PRETTY_FUNCTION__ << " backup write " << blob.size() << " bytes failed";
304 return false;
305 }
306
307 bool ok = true;
308 for (size_t i = 0; i < metadata.geometry.metadata_slot_count; i++) {
309 ok &= WriteMetadata(fd, metadata, i, metadata_blob, DefaultWriter);
310 }
311 return ok;
312 }
313
FlashPartitionTable(const std::string & super_partition,const LpMetadata & metadata)314 bool FlashPartitionTable(const std::string& super_partition, const LpMetadata& metadata) {
315 return FlashPartitionTable(PartitionOpener(), super_partition, metadata);
316 }
317
CompareMetadata(const LpMetadata & a,const LpMetadata & b)318 static bool CompareMetadata(const LpMetadata& a, const LpMetadata& b) {
319 return !memcmp(a.header.header_checksum, b.header.header_checksum,
320 sizeof(a.header.header_checksum));
321 }
322
UpdatePartitionTable(const IPartitionOpener & opener,const std::string & super_partition,const LpMetadata & metadata,uint32_t slot_number,const std::function<bool (int,const std::string &)> & writer)323 bool UpdatePartitionTable(const IPartitionOpener& opener, const std::string& super_partition,
324 const LpMetadata& metadata, uint32_t slot_number,
325 const std::function<bool(int, const std::string&)>& writer) {
326 android::base::unique_fd fd = opener.Open(super_partition, O_RDWR | O_SYNC);
327 if (fd < 0) {
328 PERROR << __PRETTY_FUNCTION__ << " open failed: " << super_partition;
329 return false;
330 }
331
332 std::string slot_suffix = SlotSuffixForSlotNumber(slot_number);
333
334 // Before writing geometry and/or logical partition tables, perform some
335 // basic checks that the geometry and tables are coherent, and will fit
336 // on the given block device.
337 std::string blob;
338 if (!ValidateAndSerializeMetadata(opener, metadata, slot_suffix, &blob)) {
339 return false;
340 }
341
342 // Verify that the old geometry is identical. If it's not, then we might be
343 // writing a table that was built for a different device, so we must reject
344 // it.
345 const LpMetadataGeometry& geometry = metadata.geometry;
346 LpMetadataGeometry old_geometry;
347 if (!ReadLogicalPartitionGeometry(fd, &old_geometry)) {
348 return false;
349 }
350 if (!CompareGeometry(geometry, old_geometry)) {
351 LERROR << "Incompatible geometry in new logical partition metadata";
352 return false;
353 }
354
355 // Validate the slot number now, before we call Read*Metadata.
356 if (slot_number >= geometry.metadata_slot_count) {
357 LERROR << "Invalid logical partition metadata slot number.";
358 return false;
359 }
360
361 // Try to read both existing copies of the metadata, if any.
362 std::unique_ptr<LpMetadata> primary = ReadPrimaryMetadata(fd, geometry, slot_number);
363 std::unique_ptr<LpMetadata> backup = ReadBackupMetadata(fd, geometry, slot_number);
364
365 if (primary && (!backup || !CompareMetadata(*primary.get(), *backup.get()))) {
366 // If the backup copy does not match the primary copy, we first
367 // synchronize the backup copy. This guarantees that a partial write
368 // still leaves one copy intact.
369 std::string old_blob;
370 if (!ValidateAndSerializeMetadata(opener, *primary.get(), slot_suffix, &old_blob)) {
371 LERROR << "Error serializing primary metadata to repair corrupted backup";
372 return false;
373 }
374 if (!WriteBackupMetadata(fd, metadata, slot_number, old_blob, writer)) {
375 LERROR << "Error writing primary metadata to repair corrupted backup";
376 return false;
377 }
378 } else if (backup && !primary) {
379 // The backup copy is coherent, and the primary is not. Sync it for
380 // safety.
381 std::string old_blob;
382 if (!ValidateAndSerializeMetadata(opener, *backup.get(), slot_suffix, &old_blob)) {
383 LERROR << "Error serializing backup metadata to repair corrupted primary";
384 return false;
385 }
386 if (!WritePrimaryMetadata(fd, metadata, slot_number, old_blob, writer)) {
387 LERROR << "Error writing backup metadata to repair corrupted primary";
388 return false;
389 }
390 }
391
392 // Both copies should now be in sync, so we can continue the update.
393 if (!WriteMetadata(fd, metadata, slot_number, blob, writer)) {
394 return false;
395 }
396
397 LINFO << "Updated logical partition table at slot " << slot_number << " on device "
398 << super_partition;
399 return true;
400 }
401
UpdatePartitionTable(const IPartitionOpener & opener,const std::string & super_partition,const LpMetadata & metadata,uint32_t slot_number)402 bool UpdatePartitionTable(const IPartitionOpener& opener, const std::string& super_partition,
403 const LpMetadata& metadata, uint32_t slot_number) {
404 return UpdatePartitionTable(opener, super_partition, metadata, slot_number, DefaultWriter);
405 }
406
UpdatePartitionTable(const std::string & super_partition,const LpMetadata & metadata,uint32_t slot_number)407 bool UpdatePartitionTable(const std::string& super_partition, const LpMetadata& metadata,
408 uint32_t slot_number) {
409 PartitionOpener opener;
410 return UpdatePartitionTable(opener, super_partition, metadata, slot_number, DefaultWriter);
411 }
412
413 } // namespace fs_mgr
414 } // namespace android
415