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