android_system_core/fs_mgr/liblp/utility.cpp

290 lines
9.6 KiB
C++
Raw Normal View History

/*
* 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/stat.h>
#include <unistd.h>
#if defined(__linux__)
#include <linux/fs.h>
#include <sys/ioctl.h>
#endif
#include <map>
#include <string>
#include <vector>
#include <android-base/file.h>
#include <ext4_utils/ext4_utils.h>
#include <openssl/sha.h>
#ifdef __ANDROID__
#include <cutils/android_get_control_file.h>
#endif
#include "utility.h"
namespace android {
namespace fs_mgr {
bool GetDescriptorSize(int fd, uint64_t* size) {
#if !defined(_WIN32)
struct stat s;
if (fstat(fd, &s) < 0) {
PERROR << __PRETTY_FUNCTION__ << "fstat failed";
return false;
}
if (S_ISBLK(s.st_mode)) {
*size = get_block_device_size(fd);
return *size != 0;
}
#endif
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 GetPrimaryGeometryOffset() {
return LP_PARTITION_RESERVED_BYTES;
}
int64_t GetBackupGeometryOffset() {
return GetPrimaryGeometryOffset() + LP_METADATA_GEOMETRY_SIZE;
}
int64_t GetPrimaryMetadataOffset(const LpMetadataGeometry& geometry, uint32_t slot_number) {
CHECK(slot_number < geometry.metadata_slot_count);
int64_t offset = LP_PARTITION_RESERVED_BYTES + (LP_METADATA_GEOMETRY_SIZE * 2) +
geometry.metadata_max_size * slot_number;
return offset;
}
int64_t GetBackupMetadataOffset(const LpMetadataGeometry& geometry, uint32_t slot_number) {
CHECK(slot_number < geometry.metadata_slot_count);
int64_t start = LP_PARTITION_RESERVED_BYTES + (LP_METADATA_GEOMETRY_SIZE * 2) +
int64_t(geometry.metadata_max_size) * geometry.metadata_slot_count;
return start + int64_t(geometry.metadata_max_size * slot_number);
}
uint64_t GetTotalMetadataSize(uint32_t metadata_max_size, uint32_t max_slots) {
return LP_PARTITION_RESERVED_BYTES +
(LP_METADATA_GEOMETRY_SIZE + metadata_max_size * max_slots) * 2;
}
const LpMetadataBlockDevice* GetMetadataSuperBlockDevice(const LpMetadata& metadata) {
if (metadata.block_devices.empty()) {
return nullptr;
}
return &metadata.block_devices[0];
}
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);
}
uint32_t SlotNumberForSlotSuffix(const std::string& suffix) {
if (suffix.empty() || suffix == "a" || suffix == "_a") {
return 0;
} else if (suffix == "b" || suffix == "_b") {
return 1;
} else {
LERROR << __PRETTY_FUNCTION__ << "slot '" << suffix
<< "' does not have a recognized format.";
return 0;
}
}
uint64_t GetTotalSuperPartitionSize(const LpMetadata& metadata) {
uint64_t size = 0;
for (const auto& block_device : metadata.block_devices) {
size += block_device.size;
}
return size;
}
std::vector<std::string> GetBlockDevicePartitionNames(const LpMetadata& metadata) {
std::vector<std::string> list;
for (const auto& block_device : metadata.block_devices) {
list.emplace_back(GetBlockDevicePartitionName(block_device));
}
return list;
}
const LpMetadataPartition* FindPartition(const LpMetadata& metadata, const std::string& name) {
for (const auto& partition : metadata.partitions) {
if (GetPartitionName(partition) == name) {
return &partition;
}
}
return nullptr;
}
uint64_t GetPartitionSize(const LpMetadata& metadata, const LpMetadataPartition& partition) {
uint64_t total_size = 0;
for (uint32_t i = 0; i < partition.num_extents; i++) {
const auto& extent = metadata.extents[partition.first_extent_index + i];
total_size += extent.num_sectors * LP_SECTOR_SIZE;
}
return total_size;
}
std::string GetPartitionSlotSuffix(const std::string& partition_name) {
if (partition_name.size() <= 2) {
return "";
}
std::string suffix = partition_name.substr(partition_name.size() - 2);
return (suffix == "_a" || suffix == "_b") ? suffix : "";
}
std::string SlotSuffixForSlotNumber(uint32_t slot_number) {
CHECK(slot_number == 0 || slot_number == 1);
return (slot_number == 0) ? "_a" : "_b";
}
bool UpdateBlockDevicePartitionName(LpMetadataBlockDevice* device, const std::string& name) {
if (name.size() > sizeof(device->partition_name)) {
return false;
}
strncpy(device->partition_name, name.c_str(), sizeof(device->partition_name));
return true;
}
liblp: Modify NewForUpdate to accomodate two super partitions. This method was designed for a single-super model, and now needs to change to accomodate two super partitions (system_a and system_b, for retrofitting). NewForUpdate is supposed to transition metadata from one block device to the next for updates. For normal devices this is a no-op, since metadata only exists on one partition (super). For retrofit devices, metadata exists on system_a and system_b. This has two implications. First, any references to the source slot must be rewritten. For example "vendor_b" must become "vendor_a". However this is not true of partition names. Partitions/extents are cleared in the updated metadata since they no longer have any meaning (the block device list has been rewritten). We also clear groups since they are re-added during OTA. The reason we have to do this rewriting is that slot suffixes are automatically applied in ReadMetadata. We do not have access to the original unsuffixed metadata that was written by the initial OTA. This was a conscious design decision, since it localizes retrofitting idiosyncracies to just a few places (ReadMetadata, NewForUpdate, and fastbootd), minimizing the number of external callers that have to understand auto-slot-suffixing. It would be arguably cleaner if retrofit metadata was always serialized *without* slot suffixes, thereby making NewForUpdate a no-op. However this would necessitate changes to the API elsewhere. The functions that read partition names would have to take a slot suffix, and this would further complicate MetadataBuilder and fastbootd. Another solution would be to augment LpMetadata to retain unsuffixed information, but this is probably not worthwhile given that retrofitting is intended to be surgical, and will have a shorter lifespan than the non-retrofit case. Bug: 116802789 Test: liblp_test gtest Change-Id: I33596d92b38c47bc70bc0aa37ed04f6f0b9d4b6f
2018-11-19 23:27:23 +00:00
bool UpdatePartitionGroupName(LpMetadataPartitionGroup* group, const std::string& name) {
if (name.size() > sizeof(group->name)) {
return false;
}
strncpy(group->name, name.c_str(), sizeof(group->name));
return true;
}
bool UpdatePartitionName(LpMetadataPartition* partition, const std::string& name) {
if (name.size() > sizeof(partition->name)) {
return false;
}
strncpy(partition->name, name.c_str(), sizeof(partition->name));
return true;
}
bool SetBlockReadonly(int fd, bool readonly) {
#if defined(__linux__)
int val = readonly;
return ioctl(fd, BLKROSET, &val) == 0;
#else
(void)fd;
(void)readonly;
return true;
#endif
}
base::unique_fd GetControlFileOrOpen(const char* path, int flags) {
#if defined(__ANDROID__)
int fd = android_get_control_file(path);
if (fd >= 0) {
int newfd = TEMP_FAILURE_RETRY(dup(fd));
if (newfd >= 0) {
return base::unique_fd(newfd);
}
PERROR << "Cannot dup fd for already controlled file: " << path << ", reopening...";
}
#endif
return base::unique_fd(open(path, flags));
}
bool UpdateMetadataForInPlaceSnapshot(LpMetadata* metadata, uint32_t source_slot_number,
uint32_t target_slot_number) {
std::string source_slot_suffix = SlotSuffixForSlotNumber(source_slot_number);
std::string target_slot_suffix = SlotSuffixForSlotNumber(target_slot_number);
// There can be leftover groups with target suffix on retrofit devices.
// They are useless now, so delete.
std::vector<LpMetadataPartitionGroup*> new_group_ptrs;
for (auto& group : metadata->groups) {
std::string group_name = GetPartitionGroupName(group);
std::string slot_suffix = GetPartitionSlotSuffix(group_name);
// Don't add groups with target slot suffix.
if (slot_suffix == target_slot_suffix) continue;
// Replace source slot suffix with target slot suffix.
if (slot_suffix == source_slot_suffix) {
std::string new_name = group_name.substr(0, group_name.size() - slot_suffix.size()) +
target_slot_suffix;
if (!UpdatePartitionGroupName(&group, new_name)) {
LERROR << "Group name too long: " << new_name;
return false;
}
}
new_group_ptrs.push_back(&group);
}
std::vector<LpMetadataPartition*> new_partition_ptrs;
for (auto& partition : metadata->partitions) {
std::string partition_name = GetPartitionName(partition);
std::string slot_suffix = GetPartitionSlotSuffix(partition_name);
// Don't add partitions with target slot suffix.
if (slot_suffix == target_slot_suffix) continue;
// Replace source slot suffix with target slot suffix.
if (slot_suffix == source_slot_suffix) {
std::string new_name =
partition_name.substr(0, partition_name.size() - slot_suffix.size()) +
target_slot_suffix;
if (!UpdatePartitionName(&partition, new_name)) {
LERROR << "Partition name too long: " << new_name;
return false;
}
}
// Update group index.
auto it = std::find(new_group_ptrs.begin(), new_group_ptrs.end(),
&metadata->groups[partition.group_index]);
if (it == new_group_ptrs.end()) {
LWARN << "Removing partition " << partition_name << " from group "
<< GetPartitionGroupName(metadata->groups[partition.group_index])
<< "; this partition should not belong to this group!";
continue; // not adding to new_partition_ptrs
}
partition.attributes |= LP_PARTITION_ATTR_UPDATED;
partition.group_index = std::distance(new_group_ptrs.begin(), it);
new_partition_ptrs.push_back(&partition);
}
std::vector<LpMetadataPartition> new_partitions;
for (auto* p : new_partition_ptrs) new_partitions.emplace_back(std::move(*p));
metadata->partitions = std::move(new_partitions);
std::vector<LpMetadataPartitionGroup> new_groups;
for (auto* g : new_group_ptrs) new_groups.emplace_back(std::move(*g));
metadata->groups = std::move(new_groups);
return true;
}
} // namespace fs_mgr
} // namespace android