Merge "fs_mgr: Add a library for managing logical partitions."
This commit is contained in:
commit
c86d3110be
11 changed files with 1625 additions and 0 deletions
|
@ -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
44
fs_mgr/liblp/Android.bp
Normal 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
351
fs_mgr/liblp/builder.cpp
Normal 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
|
169
fs_mgr/liblp/include/liblp/builder.h
Normal file
169
fs_mgr/liblp/include/liblp/builder.h
Normal 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 */
|
263
fs_mgr/liblp/include/liblp/metadata_format.h
Normal file
263
fs_mgr/liblp/include/liblp/metadata_format.h
Normal 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_ */
|
43
fs_mgr/liblp/include/liblp/reader.h
Normal file
43
fs_mgr/liblp/include/liblp/reader.h
Normal 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_ */
|
53
fs_mgr/liblp/include/liblp/writer.h
Normal file
53
fs_mgr/liblp/include/liblp/writer.h
Normal 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
307
fs_mgr/liblp/reader.cpp
Normal 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
92
fs_mgr/liblp/utility.cpp
Normal 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
56
fs_mgr/liblp/utility.h
Normal 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
244
fs_mgr/liblp/writer.cpp
Normal 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
|
Loading…
Reference in a new issue