Merge "fs_mgr: Add a library for managing logical partitions."

This commit is contained in:
Treehugger Robot 2018-06-19 23:20:16 +00:00 committed by Gerrit Code Review
commit c86d3110be
11 changed files with 1625 additions and 0 deletions

View file

@ -57,12 +57,15 @@ cc_library_static {
"libavb",
"libfstab",
"libdm",
"liblp",
],
export_static_lib_headers: [
"libfstab",
"libdm",
"liblp",
],
whole_static_libs: [
"liblp",
"liblogwrap",
"libdm",
"libfstab",

44
fs_mgr/liblp/Android.bp Normal file
View file

@ -0,0 +1,44 @@
//
// Copyright (C) 2018 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.
//
cc_library_static {
name: "liblp",
host_supported: true,
recovery_available: true,
defaults: ["fs_mgr_defaults"],
cppflags: [
"-D_FILE_OFFSET_BITS=64",
],
srcs: [
"builder.cpp",
"reader.cpp",
"utility.cpp",
"writer.cpp",
],
static_libs: [
"libbase",
"liblog",
"libcrypto",
"libcrypto_utils",
],
whole_static_libs: [
"libext2_uuid",
"libext4_utils",
"libsparse",
"libz",
],
export_include_dirs: ["include"],
}

351
fs_mgr/liblp/builder.cpp Normal file
View file

@ -0,0 +1,351 @@
/*
* Copyright (C) 2018 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.
*/
#include "liblp/builder.h"
#include <string.h>
#include <algorithm>
#include <uuid/uuid.h>
#include "liblp/metadata_format.h"
#include "utility.h"
namespace android {
namespace fs_mgr {
// Align a byte count up to the nearest 512-byte sector.
template <typename T>
static inline T AlignToSector(T value) {
return (value + (LP_SECTOR_SIZE - 1)) & ~T(LP_SECTOR_SIZE - 1);
}
void LinearExtent::AddTo(LpMetadata* out) const {
out->extents.push_back(LpMetadataExtent{num_sectors_, LP_TARGET_TYPE_LINEAR, physical_sector_});
}
void ZeroExtent::AddTo(LpMetadata* out) const {
out->extents.push_back(LpMetadataExtent{num_sectors_, LP_TARGET_TYPE_ZERO, 0});
}
Partition::Partition(const std::string& name, const std::string& guid, uint32_t attributes)
: name_(name), guid_(guid), attributes_(attributes), size_(0) {}
void Partition::AddExtent(std::unique_ptr<Extent>&& extent) {
size_ += extent->num_sectors() * LP_SECTOR_SIZE;
extents_.push_back(std::move(extent));
}
void Partition::RemoveExtents() {
size_ = 0;
extents_.clear();
}
void Partition::ShrinkTo(uint64_t requested_size) {
uint64_t aligned_size = AlignToSector(requested_size);
if (size_ <= aligned_size) {
return;
}
if (aligned_size == 0) {
RemoveExtents();
return;
}
// Remove or shrink extents of any kind until the total partition size is
// equal to the requested size.
uint64_t sectors_to_remove = (size_ - aligned_size) / LP_SECTOR_SIZE;
while (sectors_to_remove) {
Extent* extent = extents_.back().get();
if (extent->num_sectors() > sectors_to_remove) {
size_ -= sectors_to_remove * LP_SECTOR_SIZE;
extent->set_num_sectors(extent->num_sectors() - sectors_to_remove);
break;
}
size_ -= (extent->num_sectors() * LP_SECTOR_SIZE);
sectors_to_remove -= extent->num_sectors();
extents_.pop_back();
}
DCHECK(size_ == requested_size);
}
std::unique_ptr<MetadataBuilder> MetadataBuilder::New(uint64_t blockdevice_size,
uint32_t metadata_max_size,
uint32_t metadata_slot_count) {
std::unique_ptr<MetadataBuilder> builder(new MetadataBuilder());
if (!builder->Init(blockdevice_size, metadata_max_size, metadata_slot_count)) {
return nullptr;
}
return builder;
}
std::unique_ptr<MetadataBuilder> MetadataBuilder::New(const LpMetadata& metadata) {
std::unique_ptr<MetadataBuilder> builder(new MetadataBuilder());
if (!builder->Init(metadata)) {
return nullptr;
}
return builder;
}
MetadataBuilder::MetadataBuilder() {
memset(&geometry_, 0, sizeof(geometry_));
geometry_.magic = LP_METADATA_GEOMETRY_MAGIC;
geometry_.struct_size = sizeof(geometry_);
memset(&header_, 0, sizeof(header_));
header_.magic = LP_METADATA_HEADER_MAGIC;
header_.major_version = LP_METADATA_MAJOR_VERSION;
header_.minor_version = LP_METADATA_MINOR_VERSION;
header_.header_size = sizeof(header_);
header_.partitions.entry_size = sizeof(LpMetadataPartition);
header_.extents.entry_size = sizeof(LpMetadataExtent);
}
bool MetadataBuilder::Init(const LpMetadata& metadata) {
geometry_ = metadata.geometry;
for (const auto& partition : metadata.partitions) {
Partition* builder = AddPartition(GetPartitionName(partition), GetPartitionGuid(partition),
partition.attributes);
if (!builder) {
return false;
}
for (size_t i = 0; i < partition.num_extents; i++) {
const LpMetadataExtent& extent = metadata.extents[partition.first_extent_index + i];
if (extent.target_type == LP_TARGET_TYPE_LINEAR) {
auto copy = std::make_unique<LinearExtent>(extent.num_sectors, extent.target_data);
builder->AddExtent(std::move(copy));
} else if (extent.target_type == LP_TARGET_TYPE_ZERO) {
auto copy = std::make_unique<ZeroExtent>(extent.num_sectors);
builder->AddExtent(std::move(copy));
}
}
}
return true;
}
bool MetadataBuilder::Init(uint64_t blockdevice_size, uint32_t metadata_max_size,
uint32_t metadata_slot_count) {
if (metadata_max_size < sizeof(LpMetadataHeader)) {
LERROR << "Invalid metadata maximum size.";
return false;
}
if (metadata_slot_count == 0) {
LERROR << "Invalid metadata slot count.";
return false;
}
// Align the metadata size up to the nearest sector.
metadata_max_size = AlignToSector(metadata_max_size);
// We reserve a geometry block (4KB) plus space for each copy of the
// maximum size of a metadata blob. Then, we double that space since
// we store a backup copy of everything.
uint64_t reserved =
LP_METADATA_GEOMETRY_SIZE + (uint64_t(metadata_max_size) * metadata_slot_count);
uint64_t total_reserved = reserved * 2;
if (blockdevice_size < total_reserved || blockdevice_size - total_reserved < LP_SECTOR_SIZE) {
LERROR << "Attempting to create metadata on a block device that is too small.";
return false;
}
// The last sector is inclusive. We subtract one to make sure that logical
// partitions won't overlap with the same sector as the backup metadata,
// which could happen if the block device was not aligned to LP_SECTOR_SIZE.
geometry_.first_logical_sector = reserved / LP_SECTOR_SIZE;
geometry_.last_logical_sector = ((blockdevice_size - reserved) / LP_SECTOR_SIZE) - 1;
geometry_.metadata_max_size = metadata_max_size;
geometry_.metadata_slot_count = metadata_slot_count;
DCHECK(geometry_.last_logical_sector >= geometry_.first_logical_sector);
return true;
}
Partition* MetadataBuilder::AddPartition(const std::string& name, const std::string& guid,
uint32_t attributes) {
if (name.empty()) {
LERROR << "Partition must have a non-empty name.";
return nullptr;
}
if (FindPartition(name)) {
LERROR << "Attempting to create duplication partition with name: " << name;
return nullptr;
}
partitions_.push_back(std::make_unique<Partition>(name, guid, attributes));
return partitions_.back().get();
}
Partition* MetadataBuilder::FindPartition(const std::string& name) {
for (const auto& partition : partitions_) {
if (partition->name() == name) {
return partition.get();
}
}
return nullptr;
}
void MetadataBuilder::RemovePartition(const std::string& name) {
for (auto iter = partitions_.begin(); iter != partitions_.end(); iter++) {
if ((*iter)->name() == name) {
partitions_.erase(iter);
return;
}
}
}
bool MetadataBuilder::GrowPartition(Partition* partition, uint64_t requested_size) {
// Align the space needed up to the nearest sector.
uint64_t aligned_size = AlignToSector(requested_size);
if (partition->size() >= aligned_size) {
return true;
}
// Figure out how much we need to allocate.
uint64_t space_needed = aligned_size - partition->size();
uint64_t sectors_needed = space_needed / LP_SECTOR_SIZE;
DCHECK(sectors_needed * LP_SECTOR_SIZE == space_needed);
struct Interval {
uint64_t start;
uint64_t end;
Interval(uint64_t start, uint64_t end) : start(start), end(end) {}
bool operator<(const Interval& other) const { return start < other.start; }
};
std::vector<Interval> intervals;
// Collect all extents in the partition table.
for (const auto& partition : partitions_) {
for (const auto& extent : partition->extents()) {
LinearExtent* linear = extent->AsLinearExtent();
if (!linear) {
continue;
}
intervals.emplace_back(linear->physical_sector(),
linear->physical_sector() + extent->num_sectors());
}
}
// Sort extents by starting sector.
std::sort(intervals.begin(), intervals.end());
// Find gaps that we can use for new extents. Note we store new extents in a
// temporary vector, and only commit them if we are guaranteed enough free
// space.
std::vector<std::unique_ptr<LinearExtent>> new_extents;
for (size_t i = 1; i < intervals.size(); i++) {
const Interval& previous = intervals[i - 1];
const Interval& current = intervals[i];
if (previous.end >= current.start) {
// There is no gap between these two extents, try the next one. Note that
// extents may never overlap, but just for safety, we ignore them if they
// do.
DCHECK(previous.end == current.start);
continue;
}
// This gap is enough to hold the remainder of the space requested, so we
// can allocate what we need and return.
if (current.start - previous.end >= sectors_needed) {
auto extent = std::make_unique<LinearExtent>(sectors_needed, previous.end);
sectors_needed -= extent->num_sectors();
new_extents.push_back(std::move(extent));
break;
}
// This gap is not big enough to fit the remainder of the space requested,
// so consume the whole thing and keep looking for more.
auto extent = std::make_unique<LinearExtent>(current.start - previous.end, previous.end);
sectors_needed -= extent->num_sectors();
new_extents.push_back(std::move(extent));
}
// If we still have more to allocate, take it from the remaining free space
// in the allocatable region.
if (sectors_needed) {
uint64_t first_sector;
if (intervals.empty()) {
first_sector = geometry_.first_logical_sector;
} else {
first_sector = intervals.back().end;
}
DCHECK(first_sector <= geometry_.last_logical_sector);
// Note: the last usable sector is inclusive.
if (first_sector + sectors_needed > geometry_.last_logical_sector) {
LERROR << "Not enough free space to expand partition: " << partition->name();
return false;
}
auto extent = std::make_unique<LinearExtent>(sectors_needed, first_sector);
new_extents.push_back(std::move(extent));
}
for (auto& extent : new_extents) {
partition->AddExtent(std::move(extent));
}
return true;
}
void MetadataBuilder::ShrinkPartition(Partition* partition, uint64_t requested_size) {
partition->ShrinkTo(requested_size);
}
std::unique_ptr<LpMetadata> MetadataBuilder::Export() {
std::unique_ptr<LpMetadata> metadata = std::make_unique<LpMetadata>();
metadata->header = header_;
metadata->geometry = geometry_;
// Flatten the partition and extent structures into an LpMetadata, which
// makes it very easy to validate, serialize, or pass on to device-mapper.
for (const auto& partition : partitions_) {
LpMetadataPartition part;
memset(&part, 0, sizeof(part));
if (partition->name().size() > sizeof(part.name)) {
LERROR << "Partition name is too long: " << partition->name();
return nullptr;
}
if (partition->attributes() & ~(LP_PARTITION_ATTRIBUTE_MASK)) {
LERROR << "Partition " << partition->name() << " has unsupported attribute.";
return nullptr;
}
strncpy(part.name, partition->name().c_str(), sizeof(part.name));
if (uuid_parse(partition->guid().c_str(), part.guid) != 0) {
LERROR << "Could not parse guid " << partition->guid() << " for partition "
<< partition->name();
return nullptr;
}
part.first_extent_index = static_cast<uint32_t>(metadata->extents.size());
part.num_extents = static_cast<uint32_t>(partition->extents().size());
part.attributes = partition->attributes();
for (const auto& extent : partition->extents()) {
extent->AddTo(metadata.get());
}
metadata->partitions.push_back(part);
}
metadata->header.partitions.num_entries = static_cast<uint32_t>(metadata->partitions.size());
metadata->header.extents.num_entries = static_cast<uint32_t>(metadata->extents.size());
return metadata;
}
} // namespace fs_mgr
} // namespace android

View file

@ -0,0 +1,169 @@
//
// Copyright (C) 2018 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.
//
#ifndef LIBLP_METADATA_BUILDER_H
#define LIBLP_METADATA_BUILDER_H
#include <stddef.h>
#include <stdint.h>
#include <map>
#include <memory>
#include "metadata_format.h"
namespace android {
namespace fs_mgr {
class LinearExtent;
// Abstraction around dm-targets that can be encoded into logical partition tables.
class Extent {
public:
explicit Extent(uint64_t num_sectors) : num_sectors_(num_sectors) {}
virtual ~Extent() {}
virtual void AddTo(LpMetadata* out) const = 0;
virtual LinearExtent* AsLinearExtent() { return nullptr; }
uint64_t num_sectors() const { return num_sectors_; }
void set_num_sectors(uint64_t num_sectors) { num_sectors_ = num_sectors; }
protected:
uint64_t num_sectors_;
};
// This corresponds to a dm-linear target.
class LinearExtent final : public Extent {
public:
LinearExtent(uint64_t num_sectors, uint64_t physical_sector)
: Extent(num_sectors), physical_sector_(physical_sector) {}
void AddTo(LpMetadata* metadata) const override;
LinearExtent* AsLinearExtent() override { return this; }
uint64_t physical_sector() const { return physical_sector_; }
private:
uint64_t physical_sector_;
};
// This corresponds to a dm-zero target.
class ZeroExtent final : public Extent {
public:
explicit ZeroExtent(uint64_t num_sectors) : Extent(num_sectors) {}
void AddTo(LpMetadata* out) const override;
};
class Partition final {
public:
Partition(const std::string& name, const std::string& guid, uint32_t attributes);
// Add a raw extent.
void AddExtent(std::unique_ptr<Extent>&& extent);
// Remove all extents from this partition.
void RemoveExtents();
// Remove and/or shrink extents until the partition is the requested size.
// See MetadataBuilder::ShrinkPartition for more information.
void ShrinkTo(uint64_t requested_size);
const std::string& name() const { return name_; }
uint32_t attributes() const { return attributes_; }
const std::string& guid() const { return guid_; }
const std::vector<std::unique_ptr<Extent>>& extents() const { return extents_; }
uint64_t size() const { return size_; }
private:
std::string name_;
std::string guid_;
std::vector<std::unique_ptr<Extent>> extents_;
uint32_t attributes_;
uint64_t size_;
};
class MetadataBuilder {
public:
// Construct an empty logical partition table builder. The block device size
// and maximum metadata size must be specified, as this will determine which
// areas of the physical partition can be flashed for metadata vs for logical
// partitions.
//
// If the parameters would yield invalid metadata, nullptr is returned. This
// could happen if the block device size is too small to store the metadata
// and backup copies.
static std::unique_ptr<MetadataBuilder> New(uint64_t blockdevice_size,
uint32_t metadata_max_size,
uint32_t metadata_slot_count);
// Import an existing table for modification. If the table is not valid, for
// example it contains duplicate partition names, then nullptr is returned.
static std::unique_ptr<MetadataBuilder> New(const LpMetadata& metadata);
// Export metadata so it can be serialized to an image, to disk, or mounted
// via device-mapper.
std::unique_ptr<LpMetadata> Export();
// Add a partition, returning a handle so it can be sized as needed. If a
// partition with the given name already exists, nullptr is returned.
Partition* AddPartition(const std::string& name, const std::string& guid, uint32_t attributes);
// Delete a partition by name if it exists.
void RemovePartition(const std::string& name);
// Find a partition by name. If no partition is found, nullptr is returned.
Partition* FindPartition(const std::string& name);
// Grow a partition to the requested size. If the partition's size is already
// greater or equal to the requested size, this will return true and the
// partition table will not be changed. Otherwise, a greedy algorithm is
// used to find free gaps in the partition table and allocate them for this
// partition. If not enough space can be allocated, false is returned, and
// the parition table will not be modified.
//
// The size will be rounded UP to the nearest sector.
//
// Note, this is an in-memory operation, and it does not alter the
// underlying filesystem or contents of the partition on disk.
bool GrowPartition(Partition* partition, uint64_t requested_size);
// Shrink a partition to the requested size. If the partition is already
// smaller than the given size, this will return and the partition table
// will not be changed. Otherwise, extents will be removed and/or shrunk
// from the end of the partition until it is the requested size.
//
// The size will be rounded UP to the nearest sector.
//
// Note, this is an in-memory operation, and it does not alter the
// underlying filesystem or contents of the partition on disk.
void ShrinkPartition(Partition* partition, uint64_t requested_size);
private:
MetadataBuilder();
bool Init(uint64_t blockdevice_size, uint32_t metadata_max_size, uint32_t metadata_slot_count);
bool Init(const LpMetadata& metadata);
LpMetadataGeometry geometry_;
LpMetadataHeader header_;
std::vector<std::unique_ptr<Partition>> partitions_;
};
} // namespace fs_mgr
} // namespace android
#endif /* LIBLP_METADATA_BUILDER_H */

View file

@ -0,0 +1,263 @@
/*
* Copyright (C) 2018 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.
*/
#ifndef LOGICAL_PARTITION_METADATA_FORMAT_H_
#define LOGICAL_PARTITION_METADATA_FORMAT_H_
#ifdef __cplusplus
#include <string>
#include <vector>
#endif
#include <stdint.h>
#ifdef __cplusplus
extern "C" {
#endif
/* Magic signature for LpMetadataGeometry. */
#define LP_METADATA_GEOMETRY_MAGIC 0x616c4467
/* Space reserved for geometry information. */
#define LP_METADATA_GEOMETRY_SIZE 4096
/* Magic signature for LpMetadataHeader. */
#define LP_METADATA_HEADER_MAGIC 0x414C5030
/* Current metadata version. */
#define LP_METADATA_MAJOR_VERSION 1
#define LP_METADATA_MINOR_VERSION 0
/* Attributes for the LpMetadataPartition::attributes field.
*
* READONLY - The partition should not be considered writable. When used with
* device mapper, the block device will be created as read-only.
*/
#define LP_PARTITION_ATTR_READONLY 0x1
/* Mask that defines all valid attributes. */
#define LP_PARTITION_ATTRIBUTE_MASK (LP_PARTITION_ATTR_READONLY)
/* Default name of the physical partition that holds logical partition entries.
* The layout of this partition will look like:
*
* +--------------------+
* | Disk Geometry |
* +--------------------+
* | Metadata |
* +--------------------+
* | Logical Partitions |
* +--------------------+
* | Backup Metadata |
* +--------------------+
* | Geometry Backup |
* +--------------------+
*/
#define LP_METADATA_PARTITION_NAME "android"
/* Size of a sector is always 512 bytes for compatibility with the Linux kernel. */
#define LP_SECTOR_SIZE 512
/* This structure is stored at sector 0 in the first 4096 bytes of the
* partition, and again in the very last 4096 bytes. It is never modified and
* describes how logical partition information can be located.
*/
typedef struct LpMetadataGeometry {
/* 0: Magic signature (LP_METADATA_GEOMETRY_MAGIC). */
uint32_t magic;
/* 4: Size of the LpMetadataGeometry struct. */
uint32_t struct_size;
/* 8: SHA256 checksum of this struct, with this field set to 0. */
uint8_t checksum[32];
/* 40: Maximum amount of space a single copy of the metadata can use. */
uint32_t metadata_max_size;
/* 44: Number of copies of the metadata to keep. For A/B devices, this
* will be 2. For an A/B/C device, it would be 3, et cetera. For Non-A/B
* it will be 1. A backup copy of each slot is kept, so if this is "2",
* there will be four copies total.
*/
uint32_t metadata_slot_count;
/* 48: First usable sector for allocating logical partitions. this will be
* the first sector after the initial 4096 geometry block, followed by the
* space consumed by metadata_max_size*metadata_slot_count.
*/
uint64_t first_logical_sector;
/* 56: Last usable sector, inclusive, for allocating logical partitions.
* At the end of this sector will follow backup metadata slots and the
* backup geometry block at the very end.
*/
uint64_t last_logical_sector;
} __attribute__((packed)) LpMetadataGeometry;
/* The logical partition metadata has a number of tables; they are described
* in the header via the following structure.
*
* The size of the table can be computed by multiplying entry_size by
* num_entries, and the result must not overflow a 32-bit signed integer.
*/
typedef struct LpMetadataTableDescriptor {
/* 0: Location of the table, relative to the metadata header. */
uint32_t offset;
/* 4: Number of entries in the table. */
uint32_t num_entries;
/* 8: Size of each entry in the table, in bytes. */
uint32_t entry_size;
} __attribute__((packed)) LpMetadataTableDescriptor;
/* Binary format for the header of the logical partition metadata format.
*
* The format has three sections. The header must occur first, and the
* proceeding tables may be placed in any order after.
*
* +-----------------------------------------+
* | Header data - fixed size |
* +-----------------------------------------+
* | Partition table - variable size |
* +-----------------------------------------+
* | Partition table extents - variable size |
* +-----------------------------------------+
*
* The "Header" portion is described by LpMetadataHeader. It will always
* precede the other three blocks.
*
* All fields are stored in little-endian byte order when serialized.
*
* This struct is versioned; see the |major_version| and |minor_version|
* fields.
*/
typedef struct LpMetadataHeader {
/* 0: Four bytes equal to LP_METADATA_HEADER_MAGIC. */
uint32_t magic;
/* 4: Version number required to read this metadata. If the version is not
* equal to the library version, the metadata should be considered
* incompatible.
*/
uint16_t major_version;
/* 6: Minor version. A library supporting newer features should be able to
* read metadata with an older minor version. However, an older library
* should not support reading metadata if its minor version is higher.
*/
uint16_t minor_version;
/* 8: The size of this header struct. */
uint32_t header_size;
/* 12: SHA256 checksum of the header, up to |header_size| bytes, computed as
* if this field were set to 0.
*/
uint8_t header_checksum[32];
/* 44: The total size of all tables. This size is contiguous; tables may not
* have gaps in between, and they immediately follow the header.
*/
uint32_t tables_size;
/* 48: SHA256 checksum of all table contents. */
uint8_t tables_checksum[32];
/* 80: Partition table descriptor. */
LpMetadataTableDescriptor partitions;
/* 92: Extent table descriptor. */
LpMetadataTableDescriptor extents;
} __attribute__((packed)) LpMetadataHeader;
/* This struct defines a logical partition entry, similar to what would be
* present in a GUID Partition Table.
*/
typedef struct LpMetadataPartition {
/* 0: Name of this partition in ASCII characters. Any unused characters in
* the buffer must be set to 0. Characters may only be alphanumeric or _.
* The name must include at least one ASCII character, and it must be unique
* across all partition names. The length (36) is the same as the maximum
* length of a GPT partition name.
*/
char name[36];
/* 36: Globally unique identifier (GUID) of this partition. */
uint8_t guid[16];
/* 52: Attributes for the partition (see LP_PARTITION_ATTR_* flags above). */
uint32_t attributes;
/* 56: Index of the first extent owned by this partition. The extent will
* start at logical sector 0. Gaps between extents are not allowed.
*/
uint32_t first_extent_index;
/* 60: Number of extents in the partition. Every partition must have at
* least one extent.
*/
uint32_t num_extents;
} __attribute__((packed)) LpMetadataPartition;
/* This extent is a dm-linear target, and the index is an index into the
* LinearExtent table.
*/
#define LP_TARGET_TYPE_LINEAR 0
/* This extent is a dm-zero target. The index is ignored and must be 0. */
#define LP_TARGET_TYPE_ZERO 1
/* This struct defines an extent entry in the extent table block. */
typedef struct LpMetadataExtent {
/* 0: Length of this extent, in 512-byte sectors. */
uint64_t num_sectors;
/* 8: Target type for device-mapper (see LP_TARGET_TYPE_* values). */
uint32_t target_type;
/* 12: Contents depends on target_type.
*
* LINEAR: The sector on the physical partition that this extent maps onto.
* ZERO: This field must be 0.
*/
uint64_t target_data;
} __attribute__((packed)) LpMetadataExtent;
#ifdef __cplusplus
} /* extern "C" */
#endif
#ifdef __cplusplus
namespace android {
namespace fs_mgr {
// Helper structure for easily interpreting deserialized metadata, or
// re-serializing metadata.
struct LpMetadata {
LpMetadataGeometry geometry;
LpMetadataHeader header;
std::vector<LpMetadataPartition> partitions;
std::vector<LpMetadataExtent> extents;
};
// Helper to extract safe C++ strings from partition info.
std::string GetPartitionName(const LpMetadataPartition& partition);
std::string GetPartitionGuid(const LpMetadataPartition& partition);
} // namespace fs_mgr
} // namespace android
#endif
#endif /* LOGICAL_PARTITION_METADATA_FORMAT_H_ */

View file

@ -0,0 +1,43 @@
/*
* Copyright (C) 2018 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.
*/
#ifndef LIBLP_READER_H_
#define LIBLP_READER_H_
#include <stddef.h>
#include <memory>
#include "metadata_format.h"
namespace android {
namespace fs_mgr {
// Read logical partition metadata from its predetermined location on a block
// device. If readback fails, we also attempt to load from a backup copy.
std::unique_ptr<LpMetadata> ReadMetadata(const char* block_device, uint32_t slot_number);
// Read and validate the logical partition geometry from a block device.
bool ReadLogicalPartitionGeometry(const char* block_device, LpMetadataGeometry* geometry);
// Read logical partition metadata from an image file that was created with
// WriteToImageFile().
std::unique_ptr<LpMetadata> ReadFromImageFile(const char* file);
} // namespace fs_mgr
} // namespace android
#endif /* LIBLP_READER_H_ */

View file

@ -0,0 +1,53 @@
/*
* Copyright (C) 2018 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.
*/
#ifndef LIBLP_WRITER_H
#define LIBLP_WRITER_H
#include "metadata_format.h"
namespace android {
namespace fs_mgr {
// When flashing the initial logical partition layout, we also write geometry
// information at the start and end of the big physical partition. This helps
// locate metadata and backup metadata in the case of corruption or a failed
// update. For normal changes to the metadata, we never modify the geometry.
enum class SyncMode {
// Write geometry information.
Flash,
// Normal update of a single slot.
Update
};
// Write the given partition table to the given block device, writing only
// copies according to the given sync mode.
//
// This will perform some verification, such that the device has enough space
// to store the metadata as well as all of its extents.
//
// The slot number indicates which metadata slot to use.
bool WritePartitionTable(const char* block_device, const LpMetadata& metadata, SyncMode sync_mode,
uint32_t slot_number);
// Helper function to serialize geometry and metadata to a normal file, for
// flashing or debugging.
bool WriteToImageFile(const char* file, const LpMetadata& metadata);
} // namespace fs_mgr
} // namespace android
#endif /* LIBLP_WRITER_H */

307
fs_mgr/liblp/reader.cpp Normal file
View file

@ -0,0 +1,307 @@
/*
* Copyright (C) 2018 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.
*/
#include "liblp/reader.h"
#include <stddef.h>
#include <stdlib.h>
#include <unistd.h>
#include <functional>
#include <android-base/file.h>
#include <android-base/unique_fd.h>
#include "utility.h"
namespace android {
namespace fs_mgr {
// Parse an LpMetadataGeometry from a buffer. The buffer must be at least
// LP_METADATA_GEOMETRY_SIZE bytes in size.
static bool ParseGeometry(const void* buffer, LpMetadataGeometry* geometry) {
static_assert(sizeof(*geometry) <= LP_METADATA_GEOMETRY_SIZE);
memcpy(geometry, buffer, sizeof(*geometry));
// Check the magic signature.
if (geometry->magic != LP_METADATA_GEOMETRY_MAGIC) {
LERROR << "Logical partition metadata has invalid geometry magic signature.";
return false;
}
// Recompute and check the CRC32.
{
LpMetadataGeometry temp = *geometry;
memset(&temp.checksum, 0, sizeof(temp.checksum));
SHA256(&temp, sizeof(temp), temp.checksum);
if (memcmp(temp.checksum, geometry->checksum, sizeof(temp.checksum)) != 0) {
LERROR << "Logical partition metadata has invalid geometry checksum.";
return false;
}
}
// Check that the struct size is equal (this will have to change if we ever
// change the struct size in a release).
if (geometry->struct_size != sizeof(LpMetadataGeometry)) {
LERROR << "Logical partition metadata has invalid struct size.";
return false;
}
if (geometry->metadata_slot_count == 0) {
LERROR << "Logical partition metadata has invalid slot count.";
return false;
}
// Check that the metadata area and logical partition areas don't overlap.
int64_t end_of_metadata =
GetPrimaryMetadataOffset(*geometry, geometry->metadata_slot_count - 1) +
geometry->metadata_max_size;
if (uint64_t(end_of_metadata) > geometry->first_logical_sector * LP_SECTOR_SIZE) {
LERROR << "Logical partition metadata overlaps with logical partition contents.";
return false;
}
return true;
}
// Read and validate geometry information from a block device that holds
// logical partitions. If the information is corrupted, this will attempt
// to read it from a secondary backup location.
static bool ReadLogicalPartitionGeometry(int fd, LpMetadataGeometry* geometry) {
// Read the first 4096 bytes.
std::unique_ptr<uint8_t[]> buffer = std::make_unique<uint8_t[]>(LP_METADATA_GEOMETRY_SIZE);
if (SeekFile64(fd, 0, SEEK_SET) < 0) {
PERROR << __PRETTY_FUNCTION__ << "lseek failed";
return false;
}
if (!android::base::ReadFully(fd, buffer.get(), LP_METADATA_GEOMETRY_SIZE)) {
PERROR << __PRETTY_FUNCTION__ << "read " << LP_METADATA_GEOMETRY_SIZE << " bytes failed";
return false;
}
if (ParseGeometry(buffer.get(), geometry)) {
return true;
}
// Try the backup copy in the last 4096 bytes.
if (SeekFile64(fd, -LP_METADATA_GEOMETRY_SIZE, SEEK_END) < 0) {
PERROR << __PRETTY_FUNCTION__ << "lseek failed, offset " << -LP_METADATA_GEOMETRY_SIZE;
return false;
}
if (!android::base::ReadFully(fd, buffer.get(), LP_METADATA_GEOMETRY_SIZE)) {
PERROR << __PRETTY_FUNCTION__ << "backup read " << LP_METADATA_GEOMETRY_SIZE
<< " bytes failed";
return false;
}
return ParseGeometry(buffer.get(), geometry);
}
// Helper function to read geometry from a device without an open descriptor.
bool ReadLogicalPartitionGeometry(const char* block_device, LpMetadataGeometry* geometry) {
android::base::unique_fd fd(open(block_device, O_RDONLY));
if (fd < 0) {
PERROR << __PRETTY_FUNCTION__ << "open failed: " << block_device;
return false;
}
return ReadLogicalPartitionGeometry(fd, geometry);
}
static bool ValidateTableBounds(const LpMetadataHeader& header,
const LpMetadataTableDescriptor& table) {
if (table.offset > header.tables_size) {
return false;
}
uint64_t table_size = uint64_t(table.num_entries) * table.entry_size;
if (header.tables_size - table.offset < table_size) {
return false;
}
return true;
}
static bool ValidateMetadataHeader(const LpMetadataHeader& header) {
// To compute the header's checksum, we have to temporarily set its checksum
// field to 0.
{
LpMetadataHeader temp = header;
memset(&temp.header_checksum, 0, sizeof(temp.header_checksum));
SHA256(&temp, sizeof(temp), temp.header_checksum);
if (memcmp(temp.header_checksum, header.header_checksum, sizeof(temp.header_checksum)) != 0) {
LERROR << "Logical partition metadata has invalid checksum.";
return false;
}
}
// Do basic validation of key metadata bits.
if (header.magic != LP_METADATA_HEADER_MAGIC) {
LERROR << "Logical partition metadata has invalid magic value.";
return false;
}
// Check that the version is compatible.
if (header.major_version != LP_METADATA_MAJOR_VERSION ||
header.minor_version > LP_METADATA_MINOR_VERSION) {
LERROR << "Logical partition metadata has incompatible version.";
return false;
}
if (!ValidateTableBounds(header, header.partitions) ||
!ValidateTableBounds(header, header.extents)) {
LERROR << "Logical partition metadata has invalid table bounds.";
return false;
}
// Check that table entry sizes can accomodate their respective structs. If
// table sizes change, these checks will have to be adjusted.
if (header.partitions.entry_size != sizeof(LpMetadataPartition)) {
LERROR << "Logical partition metadata has invalid partition table entry size.";
return false;
}
if (header.extents.entry_size != sizeof(LpMetadataExtent)) {
LERROR << "Logical partition metadata has invalid extent table entry size.";
return false;
}
return true;
}
using ReadMetadataFn = std::function<bool(void* buffer, size_t num_bytes)>;
// Parse and validate all metadata at the current position in the given file
// descriptor.
static std::unique_ptr<LpMetadata> ParseMetadata(int fd) {
// First read and validate the header.
std::unique_ptr<LpMetadata> metadata = std::make_unique<LpMetadata>();
if (!android::base::ReadFully(fd, &metadata->header, sizeof(metadata->header))) {
PERROR << __PRETTY_FUNCTION__ << "read " << sizeof(metadata->header) << "bytes failed";
return nullptr;
}
if (!ValidateMetadataHeader(metadata->header)) {
return nullptr;
}
LpMetadataHeader& header = metadata->header;
// Read the metadata payload. Allocation is fallible in case the metadata is
// corrupt and has some huge value.
std::unique_ptr<uint8_t[]> buffer(new (std::nothrow) uint8_t[header.tables_size]);
if (!buffer) {
LERROR << "Out of memory reading logical partition tables.";
return nullptr;
}
if (!android::base::ReadFully(fd, buffer.get(), header.tables_size)) {
PERROR << __PRETTY_FUNCTION__ << "read " << header.tables_size << "bytes failed";
return nullptr;
}
uint8_t checksum[32];
SHA256(buffer.get(), header.tables_size, checksum);
if (memcmp(checksum, header.tables_checksum, sizeof(checksum)) != 0) {
LERROR << "Logical partition metadata has invalid table checksum.";
return nullptr;
}
// ValidateTableSize ensured that |cursor| is valid for the number of
// entries in the table.
uint8_t* cursor = buffer.get() + header.partitions.offset;
for (size_t i = 0; i < header.partitions.num_entries; i++) {
LpMetadataPartition partition;
memcpy(&partition, cursor, sizeof(partition));
cursor += header.partitions.entry_size;
if (partition.attributes & ~LP_PARTITION_ATTRIBUTE_MASK) {
LERROR << "Logical partition has invalid attribute set.";
return nullptr;
}
if (partition.first_extent_index + partition.num_extents > header.extents.num_entries) {
LERROR << "Logical partition has invalid extent list.";
return nullptr;
}
metadata->partitions.push_back(partition);
}
cursor = buffer.get() + header.extents.offset;
for (size_t i = 0; i < header.extents.num_entries; i++) {
LpMetadataExtent extent;
memcpy(&extent, cursor, sizeof(extent));
cursor += header.extents.entry_size;
metadata->extents.push_back(extent);
}
return metadata;
}
std::unique_ptr<LpMetadata> ReadMetadata(const char* block_device, uint32_t slot_number) {
android::base::unique_fd fd(open(block_device, O_RDONLY));
if (fd < 0) {
PERROR << __PRETTY_FUNCTION__ << "open failed: " << block_device;
return nullptr;
}
LpMetadataGeometry geometry;
if (!ReadLogicalPartitionGeometry(fd, &geometry)) {
return nullptr;
}
// First try the primary copy.
int64_t offset = GetPrimaryMetadataOffset(geometry, slot_number);
if (SeekFile64(fd, offset, SEEK_SET) < 0) {
PERROR << __PRETTY_FUNCTION__ << "lseek failed: offset " << offset;
return nullptr;
}
if (std::unique_ptr<LpMetadata> metadata = ParseMetadata(fd)) {
return metadata;
}
// Next try the backup copy.
offset = GetBackupMetadataOffset(geometry, slot_number);
if (SeekFile64(fd, offset, SEEK_END) < 0) {
PERROR << __PRETTY_FUNCTION__ << "lseek failed: offset " << offset;
return nullptr;
}
return ParseMetadata(fd);
}
std::unique_ptr<LpMetadata> ReadFromImageFile(const char* file) {
android::base::unique_fd fd(open(file, O_RDONLY));
if (fd < 0) {
PERROR << __PRETTY_FUNCTION__ << "open failed: " << file;
return nullptr;
}
LpMetadataGeometry geometry;
if (!ReadLogicalPartitionGeometry(fd, &geometry)) {
return nullptr;
}
if (SeekFile64(fd, LP_METADATA_GEOMETRY_SIZE, SEEK_SET) < 0) {
PERROR << __PRETTY_FUNCTION__ << "lseek failed: offset " << LP_METADATA_GEOMETRY_SIZE;
return nullptr;
}
std::unique_ptr<LpMetadata> metadata = ParseMetadata(fd);
if (!metadata) {
return nullptr;
}
metadata->geometry = geometry;
return metadata;
}
static std::string NameFromFixedArray(const char* name, size_t buffer_size) {
// If the end of the buffer has a null character, it's safe to assume the
// buffer is null terminated. Otherwise, we cap the string to the input
// buffer size.
if (name[buffer_size - 1] == '\0') {
return std::string(name);
}
return std::string(name, buffer_size);
}
std::string GetPartitionName(const LpMetadataPartition& partition) {
return NameFromFixedArray(partition.name, sizeof(partition.name));
}
} // namespace fs_mgr
} // namespace android

92
fs_mgr/liblp/utility.cpp Normal file
View file

@ -0,0 +1,92 @@
/*
* Copyright (C) 2018 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.
*/
#include <fcntl.h>
#include <stdint.h>
#include <sys/ioctl.h>
#include <sys/stat.h>
#include <unistd.h>
#include <android-base/file.h>
#include <ext4_utils/ext4_utils.h>
#include <openssl/sha.h>
#include <uuid/uuid.h>
#include "utility.h"
namespace android {
namespace fs_mgr {
bool GetDescriptorSize(int fd, uint64_t* size) {
struct stat s;
if (fstat(fd, &s) < 0) {
PERROR << __PRETTY_FUNCTION__ << "fstat failed";
return false;
}
if (S_ISBLK(s.st_mode)) {
return get_block_device_size(fd);
}
int64_t result = SeekFile64(fd, 0, SEEK_END);
if (result == -1) {
PERROR << __PRETTY_FUNCTION__ << "lseek failed";
return false;
}
*size = result;
return true;
}
int64_t SeekFile64(int fd, int64_t offset, int whence) {
static_assert(sizeof(off_t) == sizeof(int64_t), "Need 64-bit lseek");
return lseek(fd, offset, whence);
}
int64_t GetPrimaryMetadataOffset(const LpMetadataGeometry& geometry, uint32_t slot_number) {
CHECK(slot_number < geometry.metadata_slot_count);
int64_t offset = LP_METADATA_GEOMETRY_SIZE + geometry.metadata_max_size * slot_number;
CHECK(offset + geometry.metadata_max_size <=
int64_t(geometry.first_logical_sector * LP_SECTOR_SIZE));
return offset;
}
int64_t GetBackupMetadataOffset(const LpMetadataGeometry& geometry, uint32_t slot_number) {
CHECK(slot_number < geometry.metadata_slot_count);
int64_t start = int64_t(-LP_METADATA_GEOMETRY_SIZE) -
int64_t(geometry.metadata_max_size) * geometry.metadata_slot_count;
return start + int64_t(geometry.metadata_max_size * slot_number);
}
void SHA256(const void* data, size_t length, uint8_t out[32]) {
SHA256_CTX c;
SHA256_Init(&c);
SHA256_Update(&c, data, length);
SHA256_Final(out, &c);
}
std::string GetPartitionGuid(const LpMetadataPartition& partition) {
// 32 hex characters, four hyphens. Unfortunately libext2_uuid provides no
// macro to assist with buffer sizing.
static const size_t kGuidLen = 36;
char buffer[kGuidLen + 1];
uuid_unparse(partition.guid, buffer);
return buffer;
}
} // namespace fs_mgr
} // namespace android

56
fs_mgr/liblp/utility.h Normal file
View file

@ -0,0 +1,56 @@
/*
* Copyright (C) 2018 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.
*/
#ifndef LIBLP_UTILITY_H
#define LIBLP_UTILITY_H
#include <stddef.h>
#include <stdint.h>
#include <sys/types.h>
#include <android-base/logging.h>
#include "liblp/metadata_format.h"
#define LP_TAG "[liblp]"
#define LERROR LOG(ERROR) << LP_TAG
#define PERROR PLOG(ERROR) << LP_TAG
namespace android {
namespace fs_mgr {
// Determine the size of a block device (or file). Logs and returns false on
// error. After calling this, the position of |fd| may have changed.
bool GetDescriptorSize(int fd, uint64_t* size);
// Return the offset of a primary metadata slot, relative to the start of the
// device.
int64_t GetPrimaryMetadataOffset(const LpMetadataGeometry& geometry, uint32_t slot_number);
// Return the offset of a backup metadata slot, relative to the end of the
// device.
int64_t GetBackupMetadataOffset(const LpMetadataGeometry& geometry, uint32_t slot_number);
// Cross-platform helper for lseek64().
int64_t SeekFile64(int fd, int64_t offset, int whence);
// Compute a SHA256 hash.
void SHA256(const void* data, size_t length, uint8_t out[32]);
} // namespace fs_mgr
} // namespace android
#endif // LIBLP_UTILITY_H

244
fs_mgr/liblp/writer.cpp Normal file
View file

@ -0,0 +1,244 @@
/*
* 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.
*/
#include <inttypes.h>
#include <unistd.h>
#include <string>
#include <android-base/file.h>
#include <android-base/unique_fd.h>
#include "liblp/reader.h"
#include "liblp/writer.h"
#include "utility.h"
namespace android {
namespace fs_mgr {
static std::string SerializeGeometry(const LpMetadataGeometry& input) {
LpMetadataGeometry geometry = input;
memset(geometry.checksum, 0, sizeof(geometry.checksum));
SHA256(&geometry, sizeof(geometry), geometry.checksum);
return std::string(reinterpret_cast<const char*>(&geometry), sizeof(geometry));
}
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 &&
g1.first_logical_sector == g2.first_logical_sector &&
g1.last_logical_sector == g2.last_logical_sector;
}
static std::string SerializeMetadata(const LpMetadata& input) {
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));
// Compute positions of tables.
header.partitions.offset = 0;
header.extents.offset = header.partitions.offset + partitions.size();
header.tables_size = header.extents.offset + extents.size();
// Compute payload checksum.
std::string tables = partitions + extents;
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.
static bool ValidateGeometryAndMetadata(const LpMetadata& metadata, uint64_t blockdevice_size,
uint64_t metadata_size) {
const LpMetadataHeader& header = metadata.header;
const LpMetadataGeometry& geometry = metadata.geometry;
// Validate the usable sector range.
if (geometry.first_logical_sector > geometry.last_logical_sector) {
LERROR << "Logical partition metadata has invalid sector range.";
return false;
}
// Make sure we're writing within the space reserved.
if (metadata_size > geometry.metadata_max_size) {
LERROR << "Logical partition metadata is too large.";
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;
if (reserved_size > blockdevice_size ||
reserved_size > geometry.first_logical_sector * LP_SECTOR_SIZE) {
LERROR << "Not enough space to store all logical partition metadata slots.";
return false;
}
if (blockdevice_size - reserved_size < (geometry.last_logical_sector + 1) * LP_SECTOR_SIZE) {
LERROR << "Not enough space to backup all logical partition metadata slots.";
return false;
}
// 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.
for (const auto& extent : metadata.extents) {
if (extent.target_type == LP_TARGET_TYPE_LINEAR) {
uint64_t physical_sector = extent.target_data;
if (physical_sector < geometry.first_logical_sector ||
physical_sector + extent.num_sectors > geometry.last_logical_sector) {
LERROR << "Extent table entry is out of bounds.";
return false;
}
}
}
return true;
}
bool WritePartitionTable(const char* block_device, const LpMetadata& metadata, SyncMode sync_mode,
uint32_t slot_number) {
android::base::unique_fd fd(open(block_device, O_RDWR | O_SYNC));
if (fd < 0) {
PERROR << __PRETTY_FUNCTION__ << "open failed: " << block_device;
return false;
}
uint64_t size;
if (!GetDescriptorSize(fd, &size)) {
return false;
}
const LpMetadataGeometry& geometry = metadata.geometry;
if (sync_mode != SyncMode::Flash) {
// Verify that the old geometry is identical. If it's not, then we've
// based this new metadata on invalid assumptions.
LpMetadataGeometry old_geometry;
if (!ReadLogicalPartitionGeometry(block_device, &old_geometry)) {
return false;
}
if (!CompareGeometry(geometry, old_geometry)) {
LERROR << "Incompatible geometry in new logical partition metadata";
return false;
}
}
// Make sure we're writing to a valid metadata slot.
if (slot_number >= geometry.metadata_slot_count) {
LERROR << "Invalid logical partition metadata slot number.";
return false;
}
// 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 = SerializeMetadata(metadata);
if (!ValidateGeometryAndMetadata(metadata, size, blob.size())) {
return false;
}
// First write geometry if this is a flash operation. It gets written to
// the first and last 4096-byte regions of the device.
if (sync_mode == SyncMode::Flash) {
std::string blob = SerializeGeometry(metadata.geometry);
if (SeekFile64(fd, 0, SEEK_SET) < 0) {
PERROR << __PRETTY_FUNCTION__ << "lseek failed: offset 0";
return false;
}
if (!android::base::WriteFully(fd, blob.data(), blob.size())) {
PERROR << __PRETTY_FUNCTION__ << "write " << blob.size()
<< " bytes failed: " << block_device;
return false;
}
if (SeekFile64(fd, -LP_METADATA_GEOMETRY_SIZE, SEEK_END) < 0) {
PERROR << __PRETTY_FUNCTION__ << "lseek failed: offset " << -LP_METADATA_GEOMETRY_SIZE;
return false;
}
if (!android::base::WriteFully(fd, blob.data(), blob.size())) {
PERROR << __PRETTY_FUNCTION__ << "backup write " << blob.size()
<< " bytes failed: " << block_device;
return false;
}
}
// Write the primary copy of the metadata.
int64_t primary_offset = GetPrimaryMetadataOffset(geometry, slot_number);
if (SeekFile64(fd, primary_offset, SEEK_SET) < 0) {
PERROR << __PRETTY_FUNCTION__ << "lseek failed: offset " << primary_offset;
return false;
}
if (!android::base::WriteFully(fd, blob.data(), blob.size())) {
PERROR << __PRETTY_FUNCTION__ << "write " << blob.size()
<< " bytes failed: " << block_device;
return false;
}
// Write the backup copy of the metadata.
int64_t backup_offset = GetBackupMetadataOffset(geometry, slot_number);
int64_t abs_offset = SeekFile64(fd, backup_offset, SEEK_END);
if (abs_offset == (int64_t)-1) {
PERROR << __PRETTY_FUNCTION__ << "lseek failed: offset " << backup_offset;
return false;
}
if (abs_offset < int64_t((geometry.last_logical_sector + 1) * LP_SECTOR_SIZE)) {
PERROR << __PRETTY_FUNCTION__ << "backup offset " << abs_offset
<< " is within logical partition bounds, sector " << geometry.last_logical_sector;
return false;
}
if (!android::base::WriteFully(fd, blob.data(), blob.size())) {
PERROR << __PRETTY_FUNCTION__ << "backup write " << blob.size()
<< " bytes failed: " << block_device;
return false;
}
return true;
}
bool WriteToImageFile(const char* file, const LpMetadata& input) {
std::string geometry = SerializeGeometry(input.geometry);
std::string padding(LP_METADATA_GEOMETRY_SIZE - geometry.size(), '\0');
std::string metadata = SerializeMetadata(input);
std::string everything = geometry + padding + metadata;
android::base::unique_fd fd(open(file, O_CREAT | O_RDWR | O_TRUNC, 0644));
if (fd < 0) {
PERROR << __PRETTY_FUNCTION__ << "open failed: " << file;
return false;
}
if (!android::base::WriteFully(fd, everything.data(), everything.size())) {
PERROR << __PRETTY_FUNCTION__ << "write " << everything.size() << " bytes failed: " << file;
return false;
}
return true;
}
} // namespace fs_mgr
} // namespace android