Note that in addition to waiting for the path to appear, we must also wait for it to be unlinked. Otherwise, we could accidentally access an older device when opening and closing the same partition twice in a row. Bug: 114198005 Test: fastboot flashall works Change-Id: Iddffc34e1ac8aa066c28e7b1a92b09b6dfd7945c
161 lines
5.3 KiB
C++
161 lines
5.3 KiB
C++
/*
|
|
* 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 "utility.h"
|
|
|
|
#include <dirent.h>
|
|
#include <sys/stat.h>
|
|
#include <sys/types.h>
|
|
#include <unistd.h>
|
|
|
|
#include <android-base/logging.h>
|
|
#include <fs_mgr_dm_linear.h>
|
|
#include <liblp/liblp.h>
|
|
|
|
#include "fastboot_device.h"
|
|
|
|
using namespace android::fs_mgr;
|
|
using namespace std::chrono_literals;
|
|
using android::base::unique_fd;
|
|
using android::hardware::boot::V1_0::Slot;
|
|
|
|
static bool OpenPhysicalPartition(const std::string& name, PartitionHandle* handle) {
|
|
std::optional<std::string> path = FindPhysicalPartition(name);
|
|
if (!path) {
|
|
return false;
|
|
}
|
|
*handle = PartitionHandle(*path);
|
|
return true;
|
|
}
|
|
|
|
static bool OpenLogicalPartition(const std::string& name, const std::string& slot,
|
|
PartitionHandle* handle) {
|
|
std::optional<std::string> path = FindPhysicalPartition(LP_METADATA_PARTITION_NAME);
|
|
if (!path) {
|
|
return false;
|
|
}
|
|
uint32_t slot_number = SlotNumberForSlotSuffix(slot);
|
|
std::string dm_path;
|
|
if (!CreateLogicalPartition(path->c_str(), slot_number, name, true, 5s, &dm_path)) {
|
|
LOG(ERROR) << "Could not map partition: " << name;
|
|
return false;
|
|
}
|
|
auto closer = [name]() -> void { DestroyLogicalPartition(name, 5s); };
|
|
*handle = PartitionHandle(dm_path, std::move(closer));
|
|
return true;
|
|
}
|
|
|
|
bool OpenPartition(FastbootDevice* device, const std::string& name, PartitionHandle* handle) {
|
|
// We prioritize logical partitions over physical ones, and do this
|
|
// consistently for other partition operations (like getvar:partition-size).
|
|
if (LogicalPartitionExists(name, device->GetCurrentSlot())) {
|
|
if (!OpenLogicalPartition(name, device->GetCurrentSlot(), handle)) {
|
|
return false;
|
|
}
|
|
} else if (!OpenPhysicalPartition(name, handle)) {
|
|
LOG(ERROR) << "No such partition: " << name;
|
|
return false;
|
|
}
|
|
|
|
unique_fd fd(TEMP_FAILURE_RETRY(open(handle->path().c_str(), O_WRONLY | O_EXCL)));
|
|
if (fd < 0) {
|
|
PLOG(ERROR) << "Failed to open block device: " << handle->path();
|
|
return false;
|
|
}
|
|
handle->set_fd(std::move(fd));
|
|
return true;
|
|
}
|
|
|
|
std::optional<std::string> FindPhysicalPartition(const std::string& name) {
|
|
std::string path = "/dev/block/by-name/" + name;
|
|
if (access(path.c_str(), W_OK) < 0) {
|
|
return {};
|
|
}
|
|
return path;
|
|
}
|
|
|
|
static const LpMetadataPartition* FindLogicalPartition(const LpMetadata& metadata,
|
|
const std::string& name) {
|
|
for (const auto& partition : metadata.partitions) {
|
|
if (GetPartitionName(partition) == name) {
|
|
return &partition;
|
|
}
|
|
}
|
|
return nullptr;
|
|
}
|
|
|
|
bool LogicalPartitionExists(const std::string& name, const std::string& slot_suffix,
|
|
bool* is_zero_length) {
|
|
auto path = FindPhysicalPartition(LP_METADATA_PARTITION_NAME);
|
|
if (!path) {
|
|
return false;
|
|
}
|
|
|
|
uint32_t slot_number = SlotNumberForSlotSuffix(slot_suffix);
|
|
std::unique_ptr<LpMetadata> metadata = ReadMetadata(path->c_str(), slot_number);
|
|
if (!metadata) {
|
|
return false;
|
|
}
|
|
const LpMetadataPartition* partition = FindLogicalPartition(*metadata.get(), name);
|
|
if (!partition) {
|
|
return false;
|
|
}
|
|
if (is_zero_length) {
|
|
*is_zero_length = (partition->num_extents == 0);
|
|
}
|
|
return true;
|
|
}
|
|
|
|
bool GetSlotNumber(const std::string& slot, Slot* number) {
|
|
if (slot.size() != 1) {
|
|
return false;
|
|
}
|
|
if (slot[0] < 'a' || slot[0] > 'z') {
|
|
return false;
|
|
}
|
|
*number = slot[0] - 'a';
|
|
return true;
|
|
}
|
|
|
|
std::vector<std::string> ListPartitions(FastbootDevice* device) {
|
|
std::vector<std::string> partitions;
|
|
|
|
// First get physical partitions.
|
|
struct dirent* de;
|
|
std::unique_ptr<DIR, decltype(&closedir)> by_name(opendir("/dev/block/by-name"), closedir);
|
|
while ((de = readdir(by_name.get())) != nullptr) {
|
|
if (!strcmp(de->d_name, ".") || !strcmp(de->d_name, "..")) {
|
|
continue;
|
|
}
|
|
struct stat s;
|
|
std::string path = "/dev/block/by-name/" + std::string(de->d_name);
|
|
if (!stat(path.c_str(), &s) && S_ISBLK(s.st_mode)) {
|
|
partitions.emplace_back(de->d_name);
|
|
}
|
|
}
|
|
|
|
// Next get logical partitions.
|
|
if (auto path = FindPhysicalPartition(LP_METADATA_PARTITION_NAME)) {
|
|
uint32_t slot_number = SlotNumberForSlotSuffix(device->GetCurrentSlot());
|
|
if (auto metadata = ReadMetadata(path->c_str(), slot_number)) {
|
|
for (const auto& partition : metadata->partitions) {
|
|
std::string partition_name = GetPartitionName(partition);
|
|
partitions.emplace_back(partition_name);
|
|
}
|
|
}
|
|
}
|
|
return partitions;
|
|
}
|