1 /*
2  * Copyright (C) 2018 The Android Open Source Project
3  *
4  * Permission is hereby granted, free of charge, to any person
5  * obtaining a copy of this software and associated documentation
6  * files (the "Software"), to deal in the Software without
7  * restriction, including without limitation the rights to use, copy,
8  * modify, merge, publish, distribute, sublicense, and/or sell copies
9  * of the Software, and to permit persons to whom the Software is
10  * furnished to do so, subject to the following conditions:
11  *
12  * The above copyright notice and this permission notice shall be
13  * included in all copies or substantial portions of the Software.
14  *
15  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
16  * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
17  * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
18  * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
19  * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
20  * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
21  * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
22  * SOFTWARE.
23  */
24 
25 #include "fs_mgr_dm_linear.h"
26 
27 #include <inttypes.h>
28 #include <linux/dm-ioctl.h>
29 #include <string.h>
30 #include <sys/ioctl.h>
31 #include <sys/stat.h>
32 #include <unistd.h>
33 
34 #include <sstream>
35 
36 #include <android-base/file.h>
37 #include <android-base/logging.h>
38 #include <android-base/stringprintf.h>
39 #include <android-base/strings.h>
40 #include <android-base/unique_fd.h>
41 #include <fs_mgr/file_wait.h>
42 #include <liblp/reader.h>
43 
44 #include "fs_mgr_priv.h"
45 
46 namespace android {
47 namespace fs_mgr {
48 
49 using DeviceMapper = android::dm::DeviceMapper;
50 using DmTable = android::dm::DmTable;
51 using DmTarget = android::dm::DmTarget;
52 using DmTargetZero = android::dm::DmTargetZero;
53 using DmTargetLinear = android::dm::DmTargetLinear;
54 
GetPhysicalPartitionDevicePath(const CreateLogicalPartitionParams & params,const LpMetadataBlockDevice & block_device,const std::string & super_device,std::string * result)55 static bool GetPhysicalPartitionDevicePath(const CreateLogicalPartitionParams& params,
56                                            const LpMetadataBlockDevice& block_device,
57                                            const std::string& super_device, std::string* result) {
58     // If the super device is the source of this block device's metadata,
59     // make sure we use the correct super device (and not just "super",
60     // which might not exist.)
61     std::string name = GetBlockDevicePartitionName(block_device);
62     if (android::base::StartsWith(name, "dm-")) {
63         // Device-mapper nodes are not normally allowed in LpMetadata, since
64         // they are not consistent across reboots. However for the purposes of
65         // testing it's useful to handle them. For example when running DSUs,
66         // userdata is a device-mapper device, and some stacking will result
67         // when using libfiemap.
68         *result = "/dev/block/" + name;
69         return true;
70     }
71 
72     auto opener = params.partition_opener;
73     std::string dev_string = opener->GetDeviceString(name);
74     if (GetMetadataSuperBlockDevice(*params.metadata) == &block_device) {
75         dev_string = opener->GetDeviceString(super_device);
76     }
77 
78     // Note: device-mapper will not accept symlinks, so we must use realpath
79     // here. If the device string is a major:minor sequence, we don't need to
80     // to call Realpath (it would not work anyway).
81     if (android::base::StartsWith(dev_string, "/")) {
82         if (!android::base::Realpath(dev_string, result)) {
83             PERROR << "realpath: " << dev_string;
84             return false;
85         }
86     } else {
87         *result = dev_string;
88     }
89     return true;
90 }
91 
CreateDmTableInternal(const CreateLogicalPartitionParams & params,DmTable * table)92 bool CreateDmTableInternal(const CreateLogicalPartitionParams& params, DmTable* table) {
93     const auto& super_device = params.block_device;
94 
95     uint64_t sector = 0;
96     for (size_t i = 0; i < params.partition->num_extents; i++) {
97         const auto& extent = params.metadata->extents[params.partition->first_extent_index + i];
98         std::unique_ptr<DmTarget> target;
99         switch (extent.target_type) {
100             case LP_TARGET_TYPE_ZERO:
101                 target = std::make_unique<DmTargetZero>(sector, extent.num_sectors);
102                 break;
103             case LP_TARGET_TYPE_LINEAR: {
104                 const auto& block_device = params.metadata->block_devices[extent.target_source];
105                 std::string dev_string;
106                 if (!GetPhysicalPartitionDevicePath(params, block_device, super_device,
107                                                     &dev_string)) {
108                     LOG(ERROR) << "Unable to complete device-mapper table, unknown block device";
109                     return false;
110                 }
111                 target = std::make_unique<DmTargetLinear>(sector, extent.num_sectors, dev_string,
112                                                           extent.target_data);
113                 break;
114             }
115             default:
116                 LOG(ERROR) << "Unknown target type in metadata: " << extent.target_type;
117                 return false;
118         }
119         if (!table->AddTarget(std::move(target))) {
120             return false;
121         }
122         sector += extent.num_sectors;
123     }
124     if (params.partition->attributes & LP_PARTITION_ATTR_READONLY) {
125         table->set_readonly(true);
126     }
127     if (params.force_writable) {
128         table->set_readonly(false);
129     }
130     return true;
131 }
132 
CreateDmTable(CreateLogicalPartitionParams params,DmTable * table)133 bool CreateDmTable(CreateLogicalPartitionParams params, DmTable* table) {
134     CreateLogicalPartitionParams::OwnedData owned_data;
135     if (!params.InitDefaults(&owned_data)) return false;
136     return CreateDmTableInternal(params, table);
137 }
138 
CreateLogicalPartitions(const std::string & block_device)139 bool CreateLogicalPartitions(const std::string& block_device) {
140     uint32_t slot = SlotNumberForSlotSuffix(fs_mgr_get_slot_suffix());
141     auto metadata = ReadMetadata(block_device.c_str(), slot);
142     if (!metadata) {
143         LOG(ERROR) << "Could not read partition table.";
144         return true;
145     }
146     return CreateLogicalPartitions(*metadata.get(), block_device);
147 }
148 
ReadCurrentMetadata(const std::string & block_device)149 std::unique_ptr<LpMetadata> ReadCurrentMetadata(const std::string& block_device) {
150     uint32_t slot = SlotNumberForSlotSuffix(fs_mgr_get_slot_suffix());
151     return ReadMetadata(block_device.c_str(), slot);
152 }
153 
CreateLogicalPartitions(const LpMetadata & metadata,const std::string & super_device)154 bool CreateLogicalPartitions(const LpMetadata& metadata, const std::string& super_device) {
155     CreateLogicalPartitionParams params = {
156             .block_device = super_device,
157             .metadata = &metadata,
158     };
159     for (const auto& partition : metadata.partitions) {
160         if (!partition.num_extents) {
161             LINFO << "Skipping zero-length logical partition: " << GetPartitionName(partition);
162             continue;
163         }
164         if (partition.attributes & LP_PARTITION_ATTR_DISABLED) {
165             LINFO << "Skipping disabled partition: " << GetPartitionName(partition);
166             continue;
167         }
168 
169         params.partition = &partition;
170 
171         std::string ignore_path;
172         if (!CreateLogicalPartition(params, &ignore_path)) {
173             LERROR << "Could not create logical partition: " << GetPartitionName(partition);
174             return false;
175         }
176     }
177     return true;
178 }
179 
InitDefaults(CreateLogicalPartitionParams::OwnedData * owned)180 bool CreateLogicalPartitionParams::InitDefaults(CreateLogicalPartitionParams::OwnedData* owned) {
181     if (block_device.empty()) {
182         LOG(ERROR) << "block_device is required for CreateLogicalPartition";
183         return false;
184     }
185 
186     if (!partition_opener) {
187         owned->partition_opener = std::make_unique<PartitionOpener>();
188         partition_opener = owned->partition_opener.get();
189     }
190 
191     // Read metadata if needed.
192     if (!metadata) {
193         if (!metadata_slot) {
194             LOG(ERROR) << "Either metadata or a metadata slot must be specified.";
195             return false;
196         }
197         auto slot = *metadata_slot;
198         if (owned->metadata = ReadMetadata(*partition_opener, block_device, slot);
199             !owned->metadata) {
200             LOG(ERROR) << "Could not read partition table for: " << block_device;
201             return false;
202         }
203         metadata = owned->metadata.get();
204     }
205 
206     // Find the partition by name if needed.
207     if (!partition) {
208         for (const auto& metadata_partition : metadata->partitions) {
209             if (android::fs_mgr::GetPartitionName(metadata_partition) == partition_name) {
210                 partition = &metadata_partition;
211                 break;
212             }
213         }
214     }
215     if (!partition) {
216         LERROR << "Could not find any partition with name: " << partition_name;
217         return false;
218     }
219     if (partition_name.empty()) {
220         partition_name = android::fs_mgr::GetPartitionName(*partition);
221     } else if (partition_name != android::fs_mgr::GetPartitionName(*partition)) {
222         LERROR << "Inconsistent partition_name " << partition_name << " with partition "
223                << android::fs_mgr::GetPartitionName(*partition);
224         return false;
225     }
226 
227     if (device_name.empty()) {
228         device_name = partition_name;
229     }
230 
231     return true;
232 }
233 
CreateLogicalPartition(CreateLogicalPartitionParams params,std::string * path)234 bool CreateLogicalPartition(CreateLogicalPartitionParams params, std::string* path) {
235     CreateLogicalPartitionParams::OwnedData owned_data;
236     if (!params.InitDefaults(&owned_data)) return false;
237 
238     DmTable table;
239     if (!CreateDmTableInternal(params, &table)) {
240         return false;
241     }
242 
243     DeviceMapper& dm = DeviceMapper::Instance();
244     if (!dm.CreateDevice(params.device_name, table, path, params.timeout_ms)) {
245         return false;
246     }
247     LINFO << "Created logical partition " << params.device_name << " on device " << *path;
248     return true;
249 }
250 
GetDeviceName() const251 std::string CreateLogicalPartitionParams::GetDeviceName() const {
252     if (!device_name.empty()) return device_name;
253     return GetPartitionName();
254 }
255 
GetPartitionName() const256 std::string CreateLogicalPartitionParams::GetPartitionName() const {
257     if (!partition_name.empty()) return partition_name;
258     if (partition) return android::fs_mgr::GetPartitionName(*partition);
259     return "<unknown partition>";
260 }
261 
UnmapDevice(const std::string & name)262 bool UnmapDevice(const std::string& name) {
263     DeviceMapper& dm = DeviceMapper::Instance();
264     if (!dm.DeleteDevice(name)) {
265         return false;
266     }
267     return true;
268 }
269 
DestroyLogicalPartition(const std::string & name)270 bool DestroyLogicalPartition(const std::string& name) {
271     if (!UnmapDevice(name)) {
272         return false;
273     }
274     LINFO << "Unmapped logical partition " << name;
275     return true;
276 }
277 
278 }  // namespace fs_mgr
279 }  // namespace android
280