2018-06-07 22:36:09 +00:00
|
|
|
/*
|
|
|
|
* Copyright (C) 2007 The Android Open Source Project
|
|
|
|
*
|
|
|
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
|
|
* you may not use this file except in compliance with the License.
|
|
|
|
* You may obtain a copy of the License at
|
|
|
|
*
|
|
|
|
* http://www.apache.org/licenses/LICENSE-2.0
|
|
|
|
*
|
|
|
|
* Unless required by applicable law or agreed to in writing, software
|
|
|
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
|
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
|
|
* See the License for the specific language governing permissions and
|
|
|
|
* limitations under the License.
|
|
|
|
*/
|
|
|
|
|
2018-07-12 20:05:46 +00:00
|
|
|
#include "writer.h"
|
|
|
|
|
2018-06-07 22:36:09 +00:00
|
|
|
#include <inttypes.h>
|
|
|
|
#include <unistd.h>
|
|
|
|
|
|
|
|
#include <string>
|
|
|
|
|
|
|
|
#include <android-base/file.h>
|
|
|
|
#include <android-base/unique_fd.h>
|
|
|
|
|
2018-07-12 20:05:46 +00:00
|
|
|
#include "reader.h"
|
2018-06-07 22:36:09 +00:00
|
|
|
#include "utility.h"
|
|
|
|
|
|
|
|
namespace android {
|
|
|
|
namespace fs_mgr {
|
|
|
|
|
2018-07-13 21:25:39 +00:00
|
|
|
std::string SerializeGeometry(const LpMetadataGeometry& input) {
|
2018-06-07 22:36:09 +00:00
|
|
|
LpMetadataGeometry geometry = input;
|
|
|
|
memset(geometry.checksum, 0, sizeof(geometry.checksum));
|
|
|
|
SHA256(&geometry, sizeof(geometry), geometry.checksum);
|
2018-07-17 22:32:00 +00:00
|
|
|
|
|
|
|
std::string blob(reinterpret_cast<const char*>(&geometry), sizeof(geometry));
|
|
|
|
blob.resize(LP_METADATA_GEOMETRY_SIZE);
|
|
|
|
return blob;
|
2018-06-07 22:36:09 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
static bool CompareGeometry(const LpMetadataGeometry& g1, const LpMetadataGeometry& g2) {
|
|
|
|
return g1.metadata_max_size == g2.metadata_max_size &&
|
|
|
|
g1.metadata_slot_count == g2.metadata_slot_count &&
|
2018-10-12 20:51:52 +00:00
|
|
|
g1.logical_block_size == g2.logical_block_size;
|
2018-06-07 22:36:09 +00:00
|
|
|
}
|
|
|
|
|
2018-07-13 21:25:39 +00:00
|
|
|
std::string SerializeMetadata(const LpMetadata& input) {
|
2018-06-07 22:36:09 +00:00
|
|
|
LpMetadata metadata = input;
|
|
|
|
LpMetadataHeader& header = metadata.header;
|
|
|
|
|
|
|
|
// Serialize individual tables.
|
|
|
|
std::string partitions(reinterpret_cast<const char*>(metadata.partitions.data()),
|
|
|
|
metadata.partitions.size() * sizeof(LpMetadataPartition));
|
|
|
|
std::string extents(reinterpret_cast<const char*>(metadata.extents.data()),
|
|
|
|
metadata.extents.size() * sizeof(LpMetadataExtent));
|
2018-10-03 01:22:31 +00:00
|
|
|
std::string groups(reinterpret_cast<const char*>(metadata.groups.data()),
|
|
|
|
metadata.groups.size() * sizeof(LpMetadataPartitionGroup));
|
2018-10-12 20:51:52 +00:00
|
|
|
std::string block_devices(reinterpret_cast<const char*>(metadata.block_devices.data()),
|
|
|
|
metadata.block_devices.size() * sizeof(LpMetadataBlockDevice));
|
2018-06-07 22:36:09 +00:00
|
|
|
|
|
|
|
// Compute positions of tables.
|
|
|
|
header.partitions.offset = 0;
|
|
|
|
header.extents.offset = header.partitions.offset + partitions.size();
|
2018-10-03 01:22:31 +00:00
|
|
|
header.groups.offset = header.extents.offset + extents.size();
|
2018-10-12 20:51:52 +00:00
|
|
|
header.block_devices.offset = header.groups.offset + groups.size();
|
|
|
|
header.tables_size = header.block_devices.offset + block_devices.size();
|
2018-06-07 22:36:09 +00:00
|
|
|
|
|
|
|
// Compute payload checksum.
|
2018-10-12 20:51:52 +00:00
|
|
|
std::string tables = partitions + extents + groups + block_devices;
|
2018-06-07 22:36:09 +00:00
|
|
|
SHA256(tables.data(), tables.size(), header.tables_checksum);
|
|
|
|
|
|
|
|
// Compute header checksum.
|
|
|
|
memset(header.header_checksum, 0, sizeof(header.header_checksum));
|
|
|
|
SHA256(&header, sizeof(header), header.header_checksum);
|
|
|
|
|
|
|
|
std::string header_blob =
|
|
|
|
std::string(reinterpret_cast<const char*>(&metadata.header), sizeof(metadata.header));
|
|
|
|
return header_blob + tables;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Perform sanity checks so we don't accidentally overwrite valid metadata
|
|
|
|
// with potentially invalid metadata, or random partition data with metadata.
|
2018-07-10 16:21:33 +00:00
|
|
|
static bool ValidateAndSerializeMetadata(int fd, const LpMetadata& metadata, std::string* blob) {
|
|
|
|
uint64_t blockdevice_size;
|
|
|
|
if (!GetDescriptorSize(fd, &blockdevice_size)) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2018-06-07 22:36:09 +00:00
|
|
|
const LpMetadataHeader& header = metadata.header;
|
|
|
|
const LpMetadataGeometry& geometry = metadata.geometry;
|
2018-10-11 01:49:36 +00:00
|
|
|
|
|
|
|
*blob = SerializeMetadata(metadata);
|
|
|
|
|
2018-06-07 22:36:09 +00:00
|
|
|
// Make sure we're writing within the space reserved.
|
2018-07-10 16:21:33 +00:00
|
|
|
if (blob->size() > geometry.metadata_max_size) {
|
2018-08-31 17:15:40 +00:00
|
|
|
LERROR << "Logical partition metadata is too large. " << blob->size() << " > "
|
|
|
|
<< geometry.metadata_max_size;
|
2018-06-07 22:36:09 +00:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Make sure the device has enough space to store two backup copies of the
|
|
|
|
// metadata.
|
|
|
|
uint64_t reserved_size = LP_METADATA_GEOMETRY_SIZE +
|
|
|
|
uint64_t(geometry.metadata_max_size) * geometry.metadata_slot_count;
|
2018-10-10 21:39:22 +00:00
|
|
|
uint64_t total_reserved = reserved_size * 2;
|
|
|
|
|
2018-10-12 20:51:52 +00:00
|
|
|
const LpMetadataBlockDevice* super_device = GetMetadataSuperBlockDevice(metadata);
|
|
|
|
if (!super_device) {
|
|
|
|
LERROR << "Logical partition metadata does not have a super block device.";
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2018-10-10 21:39:22 +00:00
|
|
|
if (total_reserved > blockdevice_size ||
|
2018-10-12 20:51:52 +00:00
|
|
|
total_reserved > super_device->first_logical_sector * LP_SECTOR_SIZE) {
|
2018-06-07 22:36:09 +00:00
|
|
|
LERROR << "Not enough space to store all logical partition metadata slots.";
|
|
|
|
return false;
|
|
|
|
}
|
2018-10-12 20:51:52 +00:00
|
|
|
if (blockdevice_size != super_device->size) {
|
2018-07-13 21:36:16 +00:00
|
|
|
LERROR << "Block device size " << blockdevice_size
|
2018-10-12 20:51:52 +00:00
|
|
|
<< " does not match metadata requested size " << super_device->size;
|
2018-07-13 21:36:16 +00:00
|
|
|
return false;
|
|
|
|
}
|
2018-06-07 22:36:09 +00:00
|
|
|
|
|
|
|
// Make sure all partition entries reference valid extents.
|
|
|
|
for (const auto& partition : metadata.partitions) {
|
|
|
|
if (partition.first_extent_index + partition.num_extents > metadata.extents.size()) {
|
|
|
|
LERROR << "Partition references invalid extent.";
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Make sure all linear extents have a valid range.
|
2018-10-12 20:51:52 +00:00
|
|
|
uint64_t last_sector = super_device->size / LP_SECTOR_SIZE;
|
2018-06-07 22:36:09 +00:00
|
|
|
for (const auto& extent : metadata.extents) {
|
|
|
|
if (extent.target_type == LP_TARGET_TYPE_LINEAR) {
|
|
|
|
uint64_t physical_sector = extent.target_data;
|
2018-10-12 20:51:52 +00:00
|
|
|
if (physical_sector < super_device->first_logical_sector ||
|
2018-10-11 01:49:36 +00:00
|
|
|
physical_sector + extent.num_sectors > last_sector) {
|
2018-06-07 22:36:09 +00:00
|
|
|
LERROR << "Extent table entry is out of bounds.";
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2018-10-12 20:51:52 +00:00
|
|
|
// Check that the given region is within metadata bounds.
|
|
|
|
static bool ValidateMetadataRegion(const LpMetadata& metadata, uint64_t start, size_t size) {
|
|
|
|
const LpMetadataBlockDevice* super_device = GetMetadataSuperBlockDevice(metadata);
|
|
|
|
if (!super_device) {
|
|
|
|
LERROR << __PRETTY_FUNCTION__ << " could not locate super block device in metadata";
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
if (start + size >= super_device->first_logical_sector * LP_SECTOR_SIZE) {
|
|
|
|
LERROR << __PRETTY_FUNCTION__ << " write of " << size << " bytes at " << start
|
|
|
|
<< " overlaps with logical partition contents";
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
static bool WritePrimaryMetadata(int fd, const LpMetadata& metadata, uint32_t slot_number,
|
2018-07-12 00:08:22 +00:00
|
|
|
const std::string& blob,
|
|
|
|
const std::function<bool(int, const std::string&)>& writer) {
|
2018-10-12 20:51:52 +00:00
|
|
|
int64_t primary_offset = GetPrimaryMetadataOffset(metadata.geometry, slot_number);
|
|
|
|
if (!ValidateMetadataRegion(metadata, primary_offset, blob.size())) {
|
|
|
|
return false;
|
|
|
|
}
|
2018-06-07 22:36:09 +00:00
|
|
|
if (SeekFile64(fd, primary_offset, SEEK_SET) < 0) {
|
2018-10-12 02:10:02 +00:00
|
|
|
PERROR << __PRETTY_FUNCTION__ << " lseek failed: offset " << primary_offset;
|
2018-06-07 22:36:09 +00:00
|
|
|
return false;
|
|
|
|
}
|
2018-07-11 04:07:23 +00:00
|
|
|
if (!writer(fd, blob)) {
|
2018-10-12 02:10:02 +00:00
|
|
|
PERROR << __PRETTY_FUNCTION__ << " write " << blob.size() << " bytes failed";
|
2018-06-07 22:36:09 +00:00
|
|
|
return false;
|
|
|
|
}
|
2018-07-12 00:08:22 +00:00
|
|
|
return true;
|
|
|
|
}
|
2018-06-07 22:36:09 +00:00
|
|
|
|
2018-10-12 20:51:52 +00:00
|
|
|
static bool WriteBackupMetadata(int fd, const LpMetadata& metadata, uint32_t slot_number,
|
2018-07-12 00:08:22 +00:00
|
|
|
const std::string& blob,
|
|
|
|
const std::function<bool(int, const std::string&)>& writer) {
|
2018-10-12 20:51:52 +00:00
|
|
|
int64_t backup_offset = GetBackupMetadataOffset(metadata.geometry, slot_number);
|
|
|
|
if (!ValidateMetadataRegion(metadata, backup_offset, blob.size())) {
|
2018-06-07 22:36:09 +00:00
|
|
|
return false;
|
|
|
|
}
|
2018-10-12 20:51:52 +00:00
|
|
|
if (SeekFile64(fd, backup_offset, SEEK_SET) < 0) {
|
|
|
|
PERROR << __PRETTY_FUNCTION__ << " lseek failed: offset " << backup_offset;
|
2018-06-07 22:36:09 +00:00
|
|
|
return false;
|
|
|
|
}
|
2018-07-11 04:07:23 +00:00
|
|
|
if (!writer(fd, blob)) {
|
2018-10-12 02:10:02 +00:00
|
|
|
PERROR << __PRETTY_FUNCTION__ << " backup write " << blob.size() << " bytes failed";
|
2018-06-07 22:36:09 +00:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2018-10-12 20:51:52 +00:00
|
|
|
static bool WriteMetadata(int fd, const LpMetadata& metadata, uint32_t slot_number,
|
2018-07-12 00:08:22 +00:00
|
|
|
const std::string& blob,
|
|
|
|
const std::function<bool(int, const std::string&)>& writer) {
|
|
|
|
// Make sure we're writing to a valid metadata slot.
|
2018-10-12 20:51:52 +00:00
|
|
|
if (slot_number >= metadata.geometry.metadata_slot_count) {
|
2018-07-12 00:08:22 +00:00
|
|
|
LERROR << "Invalid logical partition metadata slot number.";
|
|
|
|
return false;
|
|
|
|
}
|
2018-10-12 20:51:52 +00:00
|
|
|
if (!WritePrimaryMetadata(fd, metadata, slot_number, blob, writer)) {
|
2018-07-12 00:08:22 +00:00
|
|
|
return false;
|
|
|
|
}
|
2018-10-12 20:51:52 +00:00
|
|
|
if (!WriteBackupMetadata(fd, metadata, slot_number, blob, writer)) {
|
2018-07-12 00:08:22 +00:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
static bool DefaultWriter(int fd, const std::string& blob) {
|
|
|
|
return android::base::WriteFully(fd, blob.data(), blob.size());
|
|
|
|
}
|
|
|
|
|
2018-10-23 01:05:54 +00:00
|
|
|
bool FlashPartitionTable(const IPartitionOpener& opener, const std::string& super_partition,
|
|
|
|
const LpMetadata& metadata) {
|
|
|
|
android::base::unique_fd fd = opener.Open(super_partition, O_RDWR | O_SYNC);
|
|
|
|
if (fd < 0) {
|
|
|
|
PERROR << __PRETTY_FUNCTION__ << " open failed: " << super_partition;
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2018-07-10 16:21:33 +00:00
|
|
|
// Before writing geometry and/or logical partition tables, perform some
|
|
|
|
// basic checks that the geometry and tables are coherent, and will fit
|
|
|
|
// on the given block device.
|
|
|
|
std::string metadata_blob;
|
|
|
|
if (!ValidateAndSerializeMetadata(fd, metadata, &metadata_blob)) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2018-10-12 02:10:02 +00:00
|
|
|
// Write zeroes to the first block.
|
|
|
|
std::string zeroes(LP_PARTITION_RESERVED_BYTES, 0);
|
|
|
|
if (SeekFile64(fd, 0, SEEK_SET) < 0) {
|
|
|
|
PERROR << __PRETTY_FUNCTION__ << " lseek failed: offset 0";
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
if (!android::base::WriteFully(fd, zeroes.data(), zeroes.size())) {
|
|
|
|
PERROR << __PRETTY_FUNCTION__ << " write " << zeroes.size() << " bytes failed";
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2018-10-23 01:05:54 +00:00
|
|
|
LWARN << "Flashing new logical partition geometry to " << super_partition;
|
|
|
|
|
2018-10-12 02:10:02 +00:00
|
|
|
// Write geometry to the primary and backup locations.
|
2018-07-10 16:21:33 +00:00
|
|
|
std::string blob = SerializeGeometry(metadata.geometry);
|
2018-10-10 21:39:22 +00:00
|
|
|
if (SeekFile64(fd, GetPrimaryGeometryOffset(), SEEK_SET) < 0) {
|
2018-10-12 02:10:02 +00:00
|
|
|
PERROR << __PRETTY_FUNCTION__ << " lseek failed: primary geometry";
|
2018-07-10 16:21:33 +00:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
if (!android::base::WriteFully(fd, blob.data(), blob.size())) {
|
2018-10-12 02:10:02 +00:00
|
|
|
PERROR << __PRETTY_FUNCTION__ << " write " << blob.size() << " bytes failed";
|
2018-07-10 16:21:33 +00:00
|
|
|
return false;
|
|
|
|
}
|
2018-10-10 21:39:22 +00:00
|
|
|
if (SeekFile64(fd, GetBackupGeometryOffset(), SEEK_SET) < 0) {
|
2018-10-12 02:10:02 +00:00
|
|
|
PERROR << __PRETTY_FUNCTION__ << " lseek failed: backup geometry";
|
2018-07-10 16:21:33 +00:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
if (!android::base::WriteFully(fd, blob.data(), blob.size())) {
|
2018-10-12 02:10:02 +00:00
|
|
|
PERROR << __PRETTY_FUNCTION__ << " backup write " << blob.size() << " bytes failed";
|
2018-07-10 16:21:33 +00:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2018-08-15 01:17:33 +00:00
|
|
|
bool ok = true;
|
|
|
|
for (size_t i = 0; i < metadata.geometry.metadata_slot_count; i++) {
|
2018-10-12 20:51:52 +00:00
|
|
|
ok &= WriteMetadata(fd, metadata, i, metadata_blob, DefaultWriter);
|
2018-08-15 01:17:33 +00:00
|
|
|
}
|
|
|
|
return ok;
|
2018-07-10 16:21:33 +00:00
|
|
|
}
|
|
|
|
|
2018-10-23 01:05:54 +00:00
|
|
|
bool FlashPartitionTable(const std::string& super_partition, const LpMetadata& metadata) {
|
|
|
|
return FlashPartitionTable(PartitionOpener(), super_partition, metadata);
|
|
|
|
}
|
|
|
|
|
2018-07-12 00:08:22 +00:00
|
|
|
static bool CompareMetadata(const LpMetadata& a, const LpMetadata& b) {
|
|
|
|
return !memcmp(a.header.header_checksum, b.header.header_checksum,
|
|
|
|
sizeof(a.header.header_checksum));
|
|
|
|
}
|
|
|
|
|
2018-10-23 01:05:54 +00:00
|
|
|
bool UpdatePartitionTable(const IPartitionOpener& opener, const std::string& super_partition,
|
|
|
|
const LpMetadata& metadata, uint32_t slot_number,
|
2018-07-12 00:08:22 +00:00
|
|
|
const std::function<bool(int, const std::string&)>& writer) {
|
2018-10-23 01:05:54 +00:00
|
|
|
android::base::unique_fd fd = opener.Open(super_partition, O_RDWR | O_SYNC);
|
|
|
|
if (fd < 0) {
|
|
|
|
PERROR << __PRETTY_FUNCTION__ << " open failed: " << super_partition;
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2018-07-10 16:21:33 +00:00
|
|
|
// Before writing geometry and/or logical partition tables, perform some
|
|
|
|
// basic checks that the geometry and tables are coherent, and will fit
|
|
|
|
// on the given block device.
|
|
|
|
std::string blob;
|
|
|
|
if (!ValidateAndSerializeMetadata(fd, metadata, &blob)) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Verify that the old geometry is identical. If it's not, then we might be
|
|
|
|
// writing a table that was built for a different device, so we must reject
|
|
|
|
// it.
|
|
|
|
const LpMetadataGeometry& geometry = metadata.geometry;
|
|
|
|
LpMetadataGeometry old_geometry;
|
|
|
|
if (!ReadLogicalPartitionGeometry(fd, &old_geometry)) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
if (!CompareGeometry(geometry, old_geometry)) {
|
|
|
|
LERROR << "Incompatible geometry in new logical partition metadata";
|
|
|
|
return false;
|
|
|
|
}
|
2018-07-12 00:08:22 +00:00
|
|
|
|
|
|
|
// Validate the slot number now, before we call Read*Metadata.
|
|
|
|
if (slot_number >= geometry.metadata_slot_count) {
|
|
|
|
LERROR << "Invalid logical partition metadata slot number.";
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Try to read both existing copies of the metadata, if any.
|
|
|
|
std::unique_ptr<LpMetadata> primary = ReadPrimaryMetadata(fd, geometry, slot_number);
|
|
|
|
std::unique_ptr<LpMetadata> backup = ReadBackupMetadata(fd, geometry, slot_number);
|
|
|
|
|
|
|
|
if (primary && (!backup || !CompareMetadata(*primary.get(), *backup.get()))) {
|
|
|
|
// If the backup copy does not match the primary copy, we first
|
|
|
|
// synchronize the backup copy. This guarantees that a partial write
|
|
|
|
// still leaves one copy intact.
|
|
|
|
std::string old_blob;
|
|
|
|
if (!ValidateAndSerializeMetadata(fd, *primary.get(), &old_blob)) {
|
|
|
|
LERROR << "Error serializing primary metadata to repair corrupted backup";
|
|
|
|
return false;
|
|
|
|
}
|
2018-10-12 20:51:52 +00:00
|
|
|
if (!WriteBackupMetadata(fd, metadata, slot_number, old_blob, writer)) {
|
2018-07-12 00:08:22 +00:00
|
|
|
LERROR << "Error writing primary metadata to repair corrupted backup";
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
} else if (backup && !primary) {
|
|
|
|
// The backup copy is coherent, and the primary is not. Sync it for
|
|
|
|
// safety.
|
|
|
|
std::string old_blob;
|
|
|
|
if (!ValidateAndSerializeMetadata(fd, *backup.get(), &old_blob)) {
|
|
|
|
LERROR << "Error serializing primary metadata to repair corrupted backup";
|
|
|
|
return false;
|
|
|
|
}
|
2018-10-12 20:51:52 +00:00
|
|
|
if (!WritePrimaryMetadata(fd, metadata, slot_number, old_blob, writer)) {
|
2018-07-12 00:08:22 +00:00
|
|
|
LERROR << "Error writing primary metadata to repair corrupted backup";
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Both copies should now be in sync, so we can continue the update.
|
2018-10-23 01:05:54 +00:00
|
|
|
if (!WriteMetadata(fd, metadata, slot_number, blob, writer)) {
|
2018-07-10 16:21:33 +00:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2018-08-03 17:12:16 +00:00
|
|
|
LINFO << "Updated logical partition table at slot " << slot_number << " on device "
|
2018-10-23 01:05:54 +00:00
|
|
|
<< super_partition;
|
2018-08-03 17:12:16 +00:00
|
|
|
return true;
|
2018-06-19 00:54:58 +00:00
|
|
|
}
|
|
|
|
|
2018-10-23 01:05:54 +00:00
|
|
|
bool UpdatePartitionTable(const IPartitionOpener& opener, const std::string& super_partition,
|
|
|
|
const LpMetadata& metadata, uint32_t slot_number) {
|
|
|
|
return UpdatePartitionTable(opener, super_partition, metadata, slot_number, DefaultWriter);
|
|
|
|
}
|
|
|
|
|
|
|
|
bool UpdatePartitionTable(const std::string& super_partition, const LpMetadata& metadata,
|
|
|
|
uint32_t slot_number) {
|
|
|
|
PartitionOpener opener;
|
|
|
|
return UpdatePartitionTable(opener, super_partition, metadata, slot_number, DefaultWriter);
|
2018-07-11 04:07:23 +00:00
|
|
|
}
|
|
|
|
|
2018-06-07 22:36:09 +00:00
|
|
|
} // namespace fs_mgr
|
|
|
|
} // namespace android
|