Hridya Valsaraju | 31d2c26 | 2018-07-20 13:35:50 -0700 | [diff] [blame] | 1 | /* |
| 2 | * Copyright (C) 2018 The Android Open Source Project |
| 3 | * |
| 4 | * Licensed under the Apache License, Version 2.0 (the "License"); |
| 5 | * you may not use this file except in compliance with the License. |
| 6 | * You may obtain a copy of the License at |
| 7 | * |
| 8 | * http://www.apache.org/licenses/LICENSE-2.0 |
| 9 | * |
| 10 | * Unless required by applicable law or agreed to in writing, software |
| 11 | * distributed under the License is distributed on an "AS IS" BASIS, |
| 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| 13 | * See the License for the specific language governing permissions and |
| 14 | * limitations under the License. |
| 15 | */ |
| 16 | |
| 17 | #include "utility.h" |
| 18 | |
David Anderson | 0f62663 | 2018-08-31 16:44:25 -0700 | [diff] [blame] | 19 | #include <dirent.h> |
| 20 | #include <sys/stat.h> |
| 21 | #include <sys/types.h> |
| 22 | #include <unistd.h> |
| 23 | |
Hridya Valsaraju | dca328d | 2018-09-24 16:01:35 -0700 | [diff] [blame] | 24 | #include <android-base/file.h> |
David Anderson | 12211d1 | 2018-07-24 15:21:20 -0700 | [diff] [blame] | 25 | #include <android-base/logging.h> |
David Anderson | d25f1c3 | 2018-11-09 20:41:33 -0800 | [diff] [blame] | 26 | #include <android-base/properties.h> |
Hridya Valsaraju | 99f3772 | 2018-10-08 11:06:38 -0700 | [diff] [blame] | 27 | #include <android-base/strings.h> |
David Anderson | 5cbd2e4 | 2018-09-27 10:53:04 -0700 | [diff] [blame] | 28 | #include <fs_mgr.h> |
David Anderson | 2324349 | 2019-12-17 00:58:31 -0800 | [diff] [blame] | 29 | #include <fs_mgr/roots.h> |
David Anderson | 88ef0b1 | 2018-08-09 10:40:00 -0700 | [diff] [blame] | 30 | #include <fs_mgr_dm_linear.h> |
David Anderson | d25f1c3 | 2018-11-09 20:41:33 -0800 | [diff] [blame] | 31 | #include <liblp/builder.h> |
David Anderson | 88ef0b1 | 2018-08-09 10:40:00 -0700 | [diff] [blame] | 32 | #include <liblp/liblp.h> |
David Anderson | 12211d1 | 2018-07-24 15:21:20 -0700 | [diff] [blame] | 33 | |
| 34 | #include "fastboot_device.h" |
| 35 | |
David Anderson | 88ef0b1 | 2018-08-09 10:40:00 -0700 | [diff] [blame] | 36 | using namespace android::fs_mgr; |
David Anderson | c8ac4e7 | 2018-09-06 17:25:03 -0700 | [diff] [blame] | 37 | using namespace std::chrono_literals; |
David Anderson | 12211d1 | 2018-07-24 15:21:20 -0700 | [diff] [blame] | 38 | using android::base::unique_fd; |
Hridya Valsaraju | 31d2c26 | 2018-07-20 13:35:50 -0700 | [diff] [blame] | 39 | using android::hardware::boot::V1_0::Slot; |
| 40 | |
David Anderson | d25f1c3 | 2018-11-09 20:41:33 -0800 | [diff] [blame] | 41 | namespace { |
| 42 | |
| 43 | bool OpenPhysicalPartition(const std::string& name, PartitionHandle* handle) { |
David Anderson | 12211d1 | 2018-07-24 15:21:20 -0700 | [diff] [blame] | 44 | std::optional<std::string> path = FindPhysicalPartition(name); |
| 45 | if (!path) { |
| 46 | return false; |
| 47 | } |
| 48 | *handle = PartitionHandle(*path); |
| 49 | return true; |
| 50 | } |
| 51 | |
David Anderson | d25f1c3 | 2018-11-09 20:41:33 -0800 | [diff] [blame] | 52 | bool OpenLogicalPartition(FastbootDevice* device, const std::string& partition_name, |
| 53 | PartitionHandle* handle) { |
| 54 | std::string slot_suffix = GetSuperSlotSuffix(device, partition_name); |
| 55 | uint32_t slot_number = SlotNumberForSlotSuffix(slot_suffix); |
| 56 | auto path = FindPhysicalPartition(fs_mgr_get_super_partition_name(slot_number)); |
David Anderson | 88ef0b1 | 2018-08-09 10:40:00 -0700 | [diff] [blame] | 57 | if (!path) { |
| 58 | return false; |
| 59 | } |
David Anderson | 15aa954 | 2019-08-12 17:07:50 -0700 | [diff] [blame] | 60 | |
| 61 | CreateLogicalPartitionParams params = { |
| 62 | .block_device = *path, |
| 63 | .metadata_slot = slot_number, |
| 64 | .partition_name = partition_name, |
| 65 | .force_writable = true, |
| 66 | .timeout_ms = 5s, |
| 67 | }; |
David Anderson | 88ef0b1 | 2018-08-09 10:40:00 -0700 | [diff] [blame] | 68 | std::string dm_path; |
David Anderson | 15aa954 | 2019-08-12 17:07:50 -0700 | [diff] [blame] | 69 | if (!CreateLogicalPartition(params, &dm_path)) { |
David Anderson | d25f1c3 | 2018-11-09 20:41:33 -0800 | [diff] [blame] | 70 | LOG(ERROR) << "Could not map partition: " << partition_name; |
David Anderson | 88ef0b1 | 2018-08-09 10:40:00 -0700 | [diff] [blame] | 71 | return false; |
| 72 | } |
David Anderson | 470fe2b | 2019-07-10 18:09:50 -0700 | [diff] [blame] | 73 | auto closer = [partition_name]() -> void { DestroyLogicalPartition(partition_name); }; |
David Anderson | 88ef0b1 | 2018-08-09 10:40:00 -0700 | [diff] [blame] | 74 | *handle = PartitionHandle(dm_path, std::move(closer)); |
| 75 | return true; |
| 76 | } |
| 77 | |
David Anderson | d25f1c3 | 2018-11-09 20:41:33 -0800 | [diff] [blame] | 78 | } // namespace |
| 79 | |
Yifan Hong | c4073d3 | 2021-02-18 12:35:42 -0800 | [diff] [blame] | 80 | bool OpenPartition(FastbootDevice* device, const std::string& name, PartitionHandle* handle, |
Konstantin Vyshetsky | 81cc119 | 2021-11-04 10:27:06 -0700 | [diff] [blame^] | 81 | int flags) { |
David Anderson | 88ef0b1 | 2018-08-09 10:40:00 -0700 | [diff] [blame] | 82 | // We prioritize logical partitions over physical ones, and do this |
| 83 | // consistently for other partition operations (like getvar:partition-size). |
David Anderson | d25f1c3 | 2018-11-09 20:41:33 -0800 | [diff] [blame] | 84 | if (LogicalPartitionExists(device, name)) { |
| 85 | if (!OpenLogicalPartition(device, name, handle)) { |
David Anderson | 88ef0b1 | 2018-08-09 10:40:00 -0700 | [diff] [blame] | 86 | return false; |
| 87 | } |
| 88 | } else if (!OpenPhysicalPartition(name, handle)) { |
David Anderson | 12211d1 | 2018-07-24 15:21:20 -0700 | [diff] [blame] | 89 | LOG(ERROR) << "No such partition: " << name; |
| 90 | return false; |
| 91 | } |
| 92 | |
Yifan Hong | a5cee93 | 2021-02-18 16:03:59 -0800 | [diff] [blame] | 93 | flags |= (O_EXCL | O_CLOEXEC | O_BINARY); |
Yifan Hong | c4073d3 | 2021-02-18 12:35:42 -0800 | [diff] [blame] | 94 | unique_fd fd(TEMP_FAILURE_RETRY(open(handle->path().c_str(), flags))); |
David Anderson | 12211d1 | 2018-07-24 15:21:20 -0700 | [diff] [blame] | 95 | if (fd < 0) { |
| 96 | PLOG(ERROR) << "Failed to open block device: " << handle->path(); |
| 97 | return false; |
| 98 | } |
| 99 | handle->set_fd(std::move(fd)); |
| 100 | return true; |
| 101 | } |
| 102 | |
| 103 | std::optional<std::string> FindPhysicalPartition(const std::string& name) { |
Hridya Valsaraju | 99f3772 | 2018-10-08 11:06:38 -0700 | [diff] [blame] | 104 | // Check for an invalid file name |
| 105 | if (android::base::StartsWith(name, "../") || name.find("/../") != std::string::npos) { |
| 106 | return {}; |
| 107 | } |
David Anderson | 12211d1 | 2018-07-24 15:21:20 -0700 | [diff] [blame] | 108 | std::string path = "/dev/block/by-name/" + name; |
Hridya Valsaraju | 3ffed21 | 2018-09-05 12:07:33 -0700 | [diff] [blame] | 109 | if (access(path.c_str(), W_OK) < 0) { |
David Anderson | 12211d1 | 2018-07-24 15:21:20 -0700 | [diff] [blame] | 110 | return {}; |
| 111 | } |
| 112 | return path; |
| 113 | } |
| 114 | |
David Anderson | 88ef0b1 | 2018-08-09 10:40:00 -0700 | [diff] [blame] | 115 | static const LpMetadataPartition* FindLogicalPartition(const LpMetadata& metadata, |
| 116 | const std::string& name) { |
| 117 | for (const auto& partition : metadata.partitions) { |
| 118 | if (GetPartitionName(partition) == name) { |
| 119 | return &partition; |
| 120 | } |
| 121 | } |
| 122 | return nullptr; |
| 123 | } |
| 124 | |
David Anderson | d25f1c3 | 2018-11-09 20:41:33 -0800 | [diff] [blame] | 125 | bool LogicalPartitionExists(FastbootDevice* device, const std::string& name, bool* is_zero_length) { |
| 126 | std::string slot_suffix = GetSuperSlotSuffix(device, name); |
| 127 | uint32_t slot_number = SlotNumberForSlotSuffix(slot_suffix); |
| 128 | auto path = FindPhysicalPartition(fs_mgr_get_super_partition_name(slot_number)); |
David Anderson | 88ef0b1 | 2018-08-09 10:40:00 -0700 | [diff] [blame] | 129 | if (!path) { |
| 130 | return false; |
| 131 | } |
| 132 | |
David Anderson | 88ef0b1 | 2018-08-09 10:40:00 -0700 | [diff] [blame] | 133 | std::unique_ptr<LpMetadata> metadata = ReadMetadata(path->c_str(), slot_number); |
| 134 | if (!metadata) { |
| 135 | return false; |
| 136 | } |
| 137 | const LpMetadataPartition* partition = FindLogicalPartition(*metadata.get(), name); |
| 138 | if (!partition) { |
| 139 | return false; |
| 140 | } |
| 141 | if (is_zero_length) { |
| 142 | *is_zero_length = (partition->num_extents == 0); |
| 143 | } |
| 144 | return true; |
| 145 | } |
| 146 | |
Hridya Valsaraju | 31d2c26 | 2018-07-20 13:35:50 -0700 | [diff] [blame] | 147 | bool GetSlotNumber(const std::string& slot, Slot* number) { |
| 148 | if (slot.size() != 1) { |
| 149 | return false; |
| 150 | } |
| 151 | if (slot[0] < 'a' || slot[0] > 'z') { |
| 152 | return false; |
| 153 | } |
| 154 | *number = slot[0] - 'a'; |
| 155 | return true; |
| 156 | } |
David Anderson | 0f62663 | 2018-08-31 16:44:25 -0700 | [diff] [blame] | 157 | |
| 158 | std::vector<std::string> ListPartitions(FastbootDevice* device) { |
| 159 | std::vector<std::string> partitions; |
| 160 | |
| 161 | // First get physical partitions. |
| 162 | struct dirent* de; |
| 163 | std::unique_ptr<DIR, decltype(&closedir)> by_name(opendir("/dev/block/by-name"), closedir); |
| 164 | while ((de = readdir(by_name.get())) != nullptr) { |
| 165 | if (!strcmp(de->d_name, ".") || !strcmp(de->d_name, "..")) { |
| 166 | continue; |
| 167 | } |
| 168 | struct stat s; |
| 169 | std::string path = "/dev/block/by-name/" + std::string(de->d_name); |
| 170 | if (!stat(path.c_str(), &s) && S_ISBLK(s.st_mode)) { |
| 171 | partitions.emplace_back(de->d_name); |
| 172 | } |
| 173 | } |
| 174 | |
David Anderson | d25f1c3 | 2018-11-09 20:41:33 -0800 | [diff] [blame] | 175 | // Find metadata in each super partition (on retrofit devices, there will |
| 176 | // be two). |
| 177 | std::vector<std::unique_ptr<LpMetadata>> metadata_list; |
| 178 | |
| 179 | uint32_t current_slot = SlotNumberForSlotSuffix(device->GetCurrentSlot()); |
| 180 | std::string super_name = fs_mgr_get_super_partition_name(current_slot); |
| 181 | if (auto metadata = ReadMetadata(super_name, current_slot)) { |
| 182 | metadata_list.emplace_back(std::move(metadata)); |
| 183 | } |
| 184 | |
| 185 | uint32_t other_slot = (current_slot == 0) ? 1 : 0; |
| 186 | std::string other_super = fs_mgr_get_super_partition_name(other_slot); |
| 187 | if (super_name != other_super) { |
| 188 | if (auto metadata = ReadMetadata(other_super, other_slot)) { |
| 189 | metadata_list.emplace_back(std::move(metadata)); |
| 190 | } |
| 191 | } |
| 192 | |
| 193 | for (const auto& metadata : metadata_list) { |
| 194 | for (const auto& partition : metadata->partitions) { |
| 195 | std::string partition_name = GetPartitionName(partition); |
| 196 | if (std::find(partitions.begin(), partitions.end(), partition_name) == |
| 197 | partitions.end()) { |
David Anderson | 0f62663 | 2018-08-31 16:44:25 -0700 | [diff] [blame] | 198 | partitions.emplace_back(partition_name); |
| 199 | } |
| 200 | } |
| 201 | } |
| 202 | return partitions; |
| 203 | } |
Hridya Valsaraju | dca328d | 2018-09-24 16:01:35 -0700 | [diff] [blame] | 204 | |
| 205 | bool GetDeviceLockStatus() { |
Oleg Matcovschi | d0a16c2 | 2021-03-25 11:41:38 -0700 | [diff] [blame] | 206 | return android::base::GetProperty("ro.boot.verifiedbootstate", "") != "orange"; |
Hridya Valsaraju | dca328d | 2018-09-24 16:01:35 -0700 | [diff] [blame] | 207 | } |
David Anderson | d25f1c3 | 2018-11-09 20:41:33 -0800 | [diff] [blame] | 208 | |
David Anderson | 4d307b0 | 2018-12-17 17:07:34 -0800 | [diff] [blame] | 209 | bool UpdateAllPartitionMetadata(FastbootDevice* device, const std::string& super_name, |
David Anderson | d25f1c3 | 2018-11-09 20:41:33 -0800 | [diff] [blame] | 210 | const android::fs_mgr::LpMetadata& metadata) { |
David Anderson | 4d307b0 | 2018-12-17 17:07:34 -0800 | [diff] [blame] | 211 | size_t num_slots = 1; |
| 212 | auto boot_control_hal = device->boot_control_hal(); |
| 213 | if (boot_control_hal) { |
| 214 | num_slots = boot_control_hal->getNumberSlots(); |
| 215 | } |
| 216 | |
David Anderson | d25f1c3 | 2018-11-09 20:41:33 -0800 | [diff] [blame] | 217 | bool ok = true; |
David Anderson | 4d307b0 | 2018-12-17 17:07:34 -0800 | [diff] [blame] | 218 | for (size_t i = 0; i < num_slots; i++) { |
David Anderson | d25f1c3 | 2018-11-09 20:41:33 -0800 | [diff] [blame] | 219 | ok &= UpdatePartitionTable(super_name, metadata, i); |
| 220 | } |
| 221 | return ok; |
| 222 | } |
| 223 | |
| 224 | std::string GetSuperSlotSuffix(FastbootDevice* device, const std::string& partition_name) { |
| 225 | // If the super partition does not have a slot suffix, this is not a |
| 226 | // retrofit device, and we should take the current slot. |
| 227 | std::string current_slot_suffix = device->GetCurrentSlot(); |
| 228 | uint32_t current_slot_number = SlotNumberForSlotSuffix(current_slot_suffix); |
| 229 | std::string super_partition = fs_mgr_get_super_partition_name(current_slot_number); |
| 230 | if (GetPartitionSlotSuffix(super_partition).empty()) { |
| 231 | return current_slot_suffix; |
| 232 | } |
| 233 | |
| 234 | // Otherwise, infer the slot from the partition name. |
| 235 | std::string slot_suffix = GetPartitionSlotSuffix(partition_name); |
| 236 | if (!slot_suffix.empty()) { |
| 237 | return slot_suffix; |
| 238 | } |
| 239 | return current_slot_suffix; |
| 240 | } |
David Anderson | 2324349 | 2019-12-17 00:58:31 -0800 | [diff] [blame] | 241 | |
| 242 | AutoMountMetadata::AutoMountMetadata() { |
| 243 | android::fs_mgr::Fstab proc_mounts; |
| 244 | if (!ReadFstabFromFile("/proc/mounts", &proc_mounts)) { |
| 245 | LOG(ERROR) << "Could not read /proc/mounts"; |
| 246 | return; |
| 247 | } |
| 248 | |
| 249 | if (GetEntryForMountPoint(&proc_mounts, "/metadata")) { |
| 250 | mounted_ = true; |
| 251 | return; |
| 252 | } |
| 253 | |
| 254 | if (!ReadDefaultFstab(&fstab_)) { |
| 255 | LOG(ERROR) << "Could not read default fstab"; |
| 256 | return; |
| 257 | } |
| 258 | mounted_ = EnsurePathMounted(&fstab_, "/metadata"); |
| 259 | should_unmount_ = true; |
| 260 | } |
| 261 | |
| 262 | AutoMountMetadata::~AutoMountMetadata() { |
| 263 | if (mounted_ && should_unmount_) { |
| 264 | EnsurePathUnmounted(&fstab_, "/metadata"); |
| 265 | } |
| 266 | } |