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