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 
19 #include <dirent.h>
20 #include <sys/stat.h>
21 #include <sys/types.h>
22 #include <unistd.h>
23 
24 #include <android-base/file.h>
25 #include <android-base/logging.h>
26 #include <android-base/properties.h>
27 #include <android-base/strings.h>
28 #include <fs_mgr.h>
29 #include <fs_mgr/roots.h>
30 #include <fs_mgr_dm_linear.h>
31 #include <liblp/builder.h>
32 #include <liblp/liblp.h>
33 
34 #include "fastboot_device.h"
35 
36 using namespace android::fs_mgr;
37 using namespace std::chrono_literals;
38 using android::base::unique_fd;
39 using android::hardware::boot::V1_0::Slot;
40 
41 namespace {
42 
OpenPhysicalPartition(const std::string & name,PartitionHandle * handle)43 bool OpenPhysicalPartition(const std::string& name, PartitionHandle* handle) {
44     std::optional<std::string> path = FindPhysicalPartition(name);
45     if (!path) {
46         return false;
47     }
48     *handle = PartitionHandle(*path);
49     return true;
50 }
51 
OpenLogicalPartition(FastbootDevice * device,const std::string & partition_name,PartitionHandle * handle)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));
57     if (!path) {
58         return false;
59     }
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     };
68     std::string dm_path;
69     if (!CreateLogicalPartition(params, &dm_path)) {
70         LOG(ERROR) << "Could not map partition: " << partition_name;
71         return false;
72     }
73     auto closer = [partition_name]() -> void { DestroyLogicalPartition(partition_name); };
74     *handle = PartitionHandle(dm_path, std::move(closer));
75     return true;
76 }
77 
78 }  // namespace
79 
OpenPartition(FastbootDevice * device,const std::string & name,PartitionHandle * handle,bool read)80 bool OpenPartition(FastbootDevice* device, const std::string& name, PartitionHandle* handle,
81                    bool read) {
82     // We prioritize logical partitions over physical ones, and do this
83     // consistently for other partition operations (like getvar:partition-size).
84     if (LogicalPartitionExists(device, name)) {
85         if (!OpenLogicalPartition(device, name, handle)) {
86             return false;
87         }
88     } else if (!OpenPhysicalPartition(name, handle)) {
89         LOG(ERROR) << "No such partition: " << name;
90         return false;
91     }
92 
93     int flags = (read ? O_RDONLY : O_WRONLY);
94     flags |= (O_EXCL | O_CLOEXEC | O_BINARY);
95     unique_fd fd(TEMP_FAILURE_RETRY(open(handle->path().c_str(), flags)));
96     if (fd < 0) {
97         PLOG(ERROR) << "Failed to open block device: " << handle->path();
98         return false;
99     }
100     handle->set_fd(std::move(fd));
101     return true;
102 }
103 
FindPhysicalPartition(const std::string & name)104 std::optional<std::string> FindPhysicalPartition(const std::string& name) {
105     // Check for an invalid file name
106     if (android::base::StartsWith(name, "../") || name.find("/../") != std::string::npos) {
107         return {};
108     }
109     std::string path = "/dev/block/by-name/" + name;
110     if (access(path.c_str(), W_OK) < 0) {
111         return {};
112     }
113     return path;
114 }
115 
FindLogicalPartition(const LpMetadata & metadata,const std::string & name)116 static const LpMetadataPartition* FindLogicalPartition(const LpMetadata& metadata,
117                                                        const std::string& name) {
118     for (const auto& partition : metadata.partitions) {
119         if (GetPartitionName(partition) == name) {
120             return &partition;
121         }
122     }
123     return nullptr;
124 }
125 
LogicalPartitionExists(FastbootDevice * device,const std::string & name,bool * is_zero_length)126 bool LogicalPartitionExists(FastbootDevice* device, const std::string& name, bool* is_zero_length) {
127     std::string slot_suffix = GetSuperSlotSuffix(device, name);
128     uint32_t slot_number = SlotNumberForSlotSuffix(slot_suffix);
129     auto path = FindPhysicalPartition(fs_mgr_get_super_partition_name(slot_number));
130     if (!path) {
131         return false;
132     }
133 
134     std::unique_ptr<LpMetadata> metadata = ReadMetadata(path->c_str(), slot_number);
135     if (!metadata) {
136         return false;
137     }
138     const LpMetadataPartition* partition = FindLogicalPartition(*metadata.get(), name);
139     if (!partition) {
140         return false;
141     }
142     if (is_zero_length) {
143         *is_zero_length = (partition->num_extents == 0);
144     }
145     return true;
146 }
147 
GetSlotNumber(const std::string & slot,Slot * number)148 bool GetSlotNumber(const std::string& slot, Slot* number) {
149     if (slot.size() != 1) {
150         return false;
151     }
152     if (slot[0] < 'a' || slot[0] > 'z') {
153         return false;
154     }
155     *number = slot[0] - 'a';
156     return true;
157 }
158 
ListPartitions(FastbootDevice * device)159 std::vector<std::string> ListPartitions(FastbootDevice* device) {
160     std::vector<std::string> partitions;
161 
162     // First get physical partitions.
163     struct dirent* de;
164     std::unique_ptr<DIR, decltype(&closedir)> by_name(opendir("/dev/block/by-name"), closedir);
165     while ((de = readdir(by_name.get())) != nullptr) {
166         if (!strcmp(de->d_name, ".") || !strcmp(de->d_name, "..")) {
167             continue;
168         }
169         struct stat s;
170         std::string path = "/dev/block/by-name/" + std::string(de->d_name);
171         if (!stat(path.c_str(), &s) && S_ISBLK(s.st_mode)) {
172             partitions.emplace_back(de->d_name);
173         }
174     }
175 
176     // Find metadata in each super partition (on retrofit devices, there will
177     // be two).
178     std::vector<std::unique_ptr<LpMetadata>> metadata_list;
179 
180     uint32_t current_slot = SlotNumberForSlotSuffix(device->GetCurrentSlot());
181     std::string super_name = fs_mgr_get_super_partition_name(current_slot);
182     if (auto metadata = ReadMetadata(super_name, current_slot)) {
183         metadata_list.emplace_back(std::move(metadata));
184     }
185 
186     uint32_t other_slot = (current_slot == 0) ? 1 : 0;
187     std::string other_super = fs_mgr_get_super_partition_name(other_slot);
188     if (super_name != other_super) {
189         if (auto metadata = ReadMetadata(other_super, other_slot)) {
190             metadata_list.emplace_back(std::move(metadata));
191         }
192     }
193 
194     for (const auto& metadata : metadata_list) {
195         for (const auto& partition : metadata->partitions) {
196             std::string partition_name = GetPartitionName(partition);
197             if (std::find(partitions.begin(), partitions.end(), partition_name) ==
198                 partitions.end()) {
199                 partitions.emplace_back(partition_name);
200             }
201         }
202     }
203     return partitions;
204 }
205 
GetDeviceLockStatus()206 bool GetDeviceLockStatus() {
207     return android::base::GetProperty("ro.boot.verifiedbootstate", "") != "orange";
208 }
209 
UpdateAllPartitionMetadata(FastbootDevice * device,const std::string & super_name,const android::fs_mgr::LpMetadata & metadata)210 bool UpdateAllPartitionMetadata(FastbootDevice* device, const std::string& super_name,
211                                 const android::fs_mgr::LpMetadata& metadata) {
212     size_t num_slots = 1;
213     auto boot_control_hal = device->boot_control_hal();
214     if (boot_control_hal) {
215         num_slots = boot_control_hal->getNumberSlots();
216     }
217 
218     bool ok = true;
219     for (size_t i = 0; i < num_slots; i++) {
220         ok &= UpdatePartitionTable(super_name, metadata, i);
221     }
222     return ok;
223 }
224 
GetSuperSlotSuffix(FastbootDevice * device,const std::string & partition_name)225 std::string GetSuperSlotSuffix(FastbootDevice* device, const std::string& partition_name) {
226     // If the super partition does not have a slot suffix, this is not a
227     // retrofit device, and we should take the current slot.
228     std::string current_slot_suffix = device->GetCurrentSlot();
229     uint32_t current_slot_number = SlotNumberForSlotSuffix(current_slot_suffix);
230     std::string super_partition = fs_mgr_get_super_partition_name(current_slot_number);
231     if (GetPartitionSlotSuffix(super_partition).empty()) {
232         return current_slot_suffix;
233     }
234 
235     // Otherwise, infer the slot from the partition name.
236     std::string slot_suffix = GetPartitionSlotSuffix(partition_name);
237     if (!slot_suffix.empty()) {
238         return slot_suffix;
239     }
240     return current_slot_suffix;
241 }
242 
AutoMountMetadata()243 AutoMountMetadata::AutoMountMetadata() {
244     android::fs_mgr::Fstab proc_mounts;
245     if (!ReadFstabFromFile("/proc/mounts", &proc_mounts)) {
246         LOG(ERROR) << "Could not read /proc/mounts";
247         return;
248     }
249 
250     if (GetEntryForMountPoint(&proc_mounts, "/metadata")) {
251         mounted_ = true;
252         return;
253     }
254 
255     if (!ReadDefaultFstab(&fstab_)) {
256         LOG(ERROR) << "Could not read default fstab";
257         return;
258     }
259     mounted_ = EnsurePathMounted(&fstab_, "/metadata");
260     should_unmount_ = true;
261 }
262 
~AutoMountMetadata()263 AutoMountMetadata::~AutoMountMetadata() {
264     if (mounted_ && should_unmount_) {
265         EnsurePathUnmounted(&fstab_, "/metadata");
266     }
267 }
268