| /* |
| * 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; |
| } |