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 "libdm/dm.h"
18
19 #include <linux/dm-ioctl.h>
20 #include <sys/ioctl.h>
21 #include <sys/sysmacros.h>
22 #include <sys/types.h>
23
24 #include <chrono>
25 #include <functional>
26 #include <string_view>
27 #include <thread>
28
29 #include <android-base/file.h>
30 #include <android-base/logging.h>
31 #include <android-base/macros.h>
32 #include <android-base/properties.h>
33 #include <android-base/strings.h>
34 #include <uuid/uuid.h>
35
36 #include "utility.h"
37
38 namespace android {
39 namespace dm {
40
41 using namespace std::literals;
42
DeviceMapper()43 DeviceMapper::DeviceMapper() : fd_(-1) {
44 fd_ = TEMP_FAILURE_RETRY(open("/dev/device-mapper", O_RDWR | O_CLOEXEC));
45 if (fd_ < 0) {
46 PLOG(ERROR) << "Failed to open device-mapper";
47 }
48 }
49
Instance()50 DeviceMapper& DeviceMapper::Instance() {
51 static DeviceMapper instance;
52 return instance;
53 }
54
55 // Creates a new device mapper device
CreateDevice(const std::string & name,const std::string & uuid)56 bool DeviceMapper::CreateDevice(const std::string& name, const std::string& uuid) {
57 if (name.empty()) {
58 LOG(ERROR) << "Unnamed device mapper device creation is not supported";
59 return false;
60 }
61 if (name.size() >= DM_NAME_LEN) {
62 LOG(ERROR) << "[" << name << "] is too long to be device mapper name";
63 return false;
64 }
65
66 struct dm_ioctl io;
67 InitIo(&io, name);
68 if (!uuid.empty()) {
69 snprintf(io.uuid, sizeof(io.uuid), "%s", uuid.c_str());
70 }
71
72 if (ioctl(fd_, DM_DEV_CREATE, &io)) {
73 PLOG(ERROR) << "DM_DEV_CREATE failed for [" << name << "]";
74 return false;
75 }
76
77 // Check to make sure the newly created device doesn't already have targets
78 // added or opened by someone
79 CHECK(io.target_count == 0) << "Unexpected targets for newly created [" << name << "] device";
80 CHECK(io.open_count == 0) << "Unexpected opens for newly created [" << name << "] device";
81
82 // Creates a new device mapper device with the name passed in
83 return true;
84 }
85
DeleteDeviceIfExists(const std::string & name,const std::chrono::milliseconds & timeout_ms)86 bool DeviceMapper::DeleteDeviceIfExists(const std::string& name,
87 const std::chrono::milliseconds& timeout_ms) {
88 if (GetState(name) == DmDeviceState::INVALID) {
89 return true;
90 }
91 return DeleteDevice(name, timeout_ms);
92 }
93
DeleteDeviceIfExists(const std::string & name)94 bool DeviceMapper::DeleteDeviceIfExists(const std::string& name) {
95 return DeleteDeviceIfExists(name, 0ms);
96 }
97
DeleteDevice(const std::string & name,const std::chrono::milliseconds & timeout_ms)98 bool DeviceMapper::DeleteDevice(const std::string& name,
99 const std::chrono::milliseconds& timeout_ms) {
100 std::string unique_path;
101 if (!GetDeviceUniquePath(name, &unique_path)) {
102 LOG(ERROR) << "Failed to get unique path for device " << name;
103 }
104 struct dm_ioctl io;
105 InitIo(&io, name);
106
107 if (ioctl(fd_, DM_DEV_REMOVE, &io)) {
108 PLOG(ERROR) << "DM_DEV_REMOVE failed for [" << name << "]";
109 return false;
110 }
111
112 // Check to make sure appropriate uevent is generated so ueventd will
113 // do the right thing and remove the corresponding device node and symlinks.
114 CHECK(io.flags & DM_UEVENT_GENERATED_FLAG)
115 << "Didn't generate uevent for [" << name << "] removal";
116
117 if (timeout_ms <= std::chrono::milliseconds::zero()) {
118 return true;
119 }
120 if (unique_path.empty()) {
121 return false;
122 }
123 if (!WaitForFileDeleted(unique_path, timeout_ms)) {
124 LOG(ERROR) << "Failed waiting for " << unique_path << " to be deleted";
125 return false;
126 }
127 return true;
128 }
129
DeleteDevice(const std::string & name)130 bool DeviceMapper::DeleteDevice(const std::string& name) {
131 return DeleteDevice(name, 0ms);
132 }
133
GenerateUuid()134 static std::string GenerateUuid() {
135 uuid_t uuid_bytes;
136 uuid_generate(uuid_bytes);
137
138 char uuid_chars[37] = {};
139 uuid_unparse_lower(uuid_bytes, uuid_chars);
140
141 return std::string{uuid_chars};
142 }
143
IsRecovery()144 static bool IsRecovery() {
145 return access("/system/bin/recovery", F_OK) == 0;
146 }
147
CreateDevice(const std::string & name,const DmTable & table,std::string * path,const std::chrono::milliseconds & timeout_ms)148 bool DeviceMapper::CreateDevice(const std::string& name, const DmTable& table, std::string* path,
149 const std::chrono::milliseconds& timeout_ms) {
150 std::string uuid = GenerateUuid();
151 if (!CreateDevice(name, uuid)) {
152 return false;
153 }
154
155 // We use the unique path for testing whether the device is ready. After
156 // that, it's safe to use the dm-N path which is compatible with callers
157 // that expect it to be formatted as such.
158 std::string unique_path;
159 if (!LoadTableAndActivate(name, table) || !GetDeviceUniquePath(name, &unique_path) ||
160 !GetDmDevicePathByName(name, path)) {
161 DeleteDevice(name);
162 return false;
163 }
164
165 if (timeout_ms <= std::chrono::milliseconds::zero()) {
166 return true;
167 }
168
169 if (IsRecovery()) {
170 bool non_ab_device = android::base::GetProperty("ro.build.ab_update", "").empty();
171 int sdk = android::base::GetIntProperty("ro.build.version.sdk", 0);
172 if (non_ab_device && sdk && sdk <= 29) {
173 LOG(INFO) << "Detected ueventd incompatibility, reverting to legacy libdm behavior.";
174 unique_path = *path;
175 }
176 }
177
178 if (!WaitForFile(unique_path, timeout_ms)) {
179 LOG(ERROR) << "Failed waiting for device path: " << unique_path;
180 DeleteDevice(name);
181 return false;
182 }
183 return true;
184 }
185
GetDeviceUniquePath(const std::string & name,std::string * path)186 bool DeviceMapper::GetDeviceUniquePath(const std::string& name, std::string* path) {
187 struct dm_ioctl io;
188 InitIo(&io, name);
189 if (ioctl(fd_, DM_DEV_STATUS, &io) < 0) {
190 PLOG(ERROR) << "Failed to get device path: " << name;
191 return false;
192 }
193
194 if (io.uuid[0] == '\0') {
195 LOG(ERROR) << "Device does not have a unique path: " << name;
196 return false;
197 }
198 *path = "/dev/block/mapper/by-uuid/"s + io.uuid;
199 return true;
200 }
201
GetDetailedInfo(const std::string & name) const202 std::optional<DeviceMapper::Info> DeviceMapper::GetDetailedInfo(const std::string& name) const {
203 struct dm_ioctl io;
204 InitIo(&io, name);
205 if (ioctl(fd_, DM_DEV_STATUS, &io) < 0) {
206 return std::nullopt;
207 }
208 return Info(io.flags);
209 }
210
GetState(const std::string & name) const211 DmDeviceState DeviceMapper::GetState(const std::string& name) const {
212 struct dm_ioctl io;
213 InitIo(&io, name);
214 if (ioctl(fd_, DM_DEV_STATUS, &io) < 0) {
215 return DmDeviceState::INVALID;
216 }
217 if ((io.flags & DM_ACTIVE_PRESENT_FLAG) && !(io.flags & DM_SUSPEND_FLAG)) {
218 return DmDeviceState::ACTIVE;
219 }
220 return DmDeviceState::SUSPENDED;
221 }
222
ChangeState(const std::string & name,DmDeviceState state)223 bool DeviceMapper::ChangeState(const std::string& name, DmDeviceState state) {
224 if (state != DmDeviceState::SUSPENDED && state != DmDeviceState::ACTIVE) {
225 return false;
226 }
227
228 struct dm_ioctl io;
229 InitIo(&io, name);
230
231 if (state == DmDeviceState::SUSPENDED) io.flags = DM_SUSPEND_FLAG;
232
233 if (ioctl(fd_, DM_DEV_SUSPEND, &io) < 0) {
234 PLOG(ERROR) << "DM_DEV_SUSPEND "
235 << (state == DmDeviceState::SUSPENDED ? "suspend" : "resume") << " failed";
236 return false;
237 }
238 return true;
239 }
240
CreateDevice(const std::string & name,const DmTable & table)241 bool DeviceMapper::CreateDevice(const std::string& name, const DmTable& table) {
242 std::string ignore_path;
243 if (!CreateDevice(name, table, &ignore_path, 0ms)) {
244 return false;
245 }
246 return true;
247 }
248
LoadTableAndActivate(const std::string & name,const DmTable & table)249 bool DeviceMapper::LoadTableAndActivate(const std::string& name, const DmTable& table) {
250 std::string ioctl_buffer(sizeof(struct dm_ioctl), 0);
251 ioctl_buffer += table.Serialize();
252
253 struct dm_ioctl* io = reinterpret_cast<struct dm_ioctl*>(&ioctl_buffer[0]);
254 InitIo(io, name);
255 io->data_size = ioctl_buffer.size();
256 io->data_start = sizeof(struct dm_ioctl);
257 io->target_count = static_cast<uint32_t>(table.num_targets());
258 if (table.readonly()) {
259 io->flags |= DM_READONLY_FLAG;
260 }
261 if (ioctl(fd_, DM_TABLE_LOAD, io)) {
262 PLOG(ERROR) << "DM_TABLE_LOAD failed";
263 return false;
264 }
265
266 InitIo(io, name);
267 if (ioctl(fd_, DM_DEV_SUSPEND, io)) {
268 PLOG(ERROR) << "DM_TABLE_SUSPEND resume failed";
269 return false;
270 }
271 return true;
272 }
273
274 // Reads all the available device mapper targets and their corresponding
275 // versions from the kernel and returns in a vector
GetAvailableTargets(std::vector<DmTargetTypeInfo> * targets)276 bool DeviceMapper::GetAvailableTargets(std::vector<DmTargetTypeInfo>* targets) {
277 targets->clear();
278
279 // calculate the space needed to read a maximum of kMaxPossibleDmTargets
280 uint32_t payload_size = sizeof(struct dm_target_versions);
281 payload_size += DM_MAX_TYPE_NAME;
282 // device mapper wants every target spec to be aligned at 8-byte boundary
283 payload_size = DM_ALIGN(payload_size);
284 payload_size *= kMaxPossibleDmTargets;
285
286 uint32_t data_size = sizeof(struct dm_ioctl) + payload_size;
287 auto buffer = std::unique_ptr<void, void (*)(void*)>(calloc(1, data_size), free);
288 if (buffer == nullptr) {
289 LOG(ERROR) << "failed to allocate memory";
290 return false;
291 }
292
293 // Sets appropriate data size and data_start to make sure we tell kernel
294 // about the total size of the buffer we are passing and where to start
295 // writing the list of targets.
296 struct dm_ioctl* io = reinterpret_cast<struct dm_ioctl*>(buffer.get());
297 InitIo(io);
298 io->data_size = data_size;
299 io->data_start = sizeof(*io);
300
301 if (ioctl(fd_, DM_LIST_VERSIONS, io)) {
302 PLOG(ERROR) << "DM_LIST_VERSIONS failed";
303 return false;
304 }
305
306 // If the provided buffer wasn't enough to list all targets, note that
307 // any data beyond sizeof(*io) must not be read in this case
308 if (io->flags & DM_BUFFER_FULL_FLAG) {
309 LOG(INFO) << data_size << " is not enough memory to list all dm targets";
310 return false;
311 }
312
313 // if there are no targets registered, return success with empty vector
314 if (io->data_size == sizeof(*io)) {
315 return true;
316 }
317
318 // Parse each target and list the name and version
319 // TODO(b/110035986): Templatize this
320 uint32_t next = sizeof(*io);
321 data_size = io->data_size - next;
322 struct dm_target_versions* vers =
323 reinterpret_cast<struct dm_target_versions*>(static_cast<char*>(buffer.get()) + next);
324 while (next && data_size) {
325 targets->emplace_back(vers);
326 if (vers->next == 0) {
327 break;
328 }
329 next += vers->next;
330 data_size -= vers->next;
331 vers = reinterpret_cast<struct dm_target_versions*>(static_cast<char*>(buffer.get()) +
332 next);
333 }
334
335 return true;
336 }
337
GetTargetByName(const std::string & name,DmTargetTypeInfo * info)338 bool DeviceMapper::GetTargetByName(const std::string& name, DmTargetTypeInfo* info) {
339 std::vector<DmTargetTypeInfo> targets;
340 if (!GetAvailableTargets(&targets)) {
341 return false;
342 }
343 for (const auto& target : targets) {
344 if (target.name() == name) {
345 if (info) *info = target;
346 return true;
347 }
348 }
349 return false;
350 }
351
GetAvailableDevices(std::vector<DmBlockDevice> * devices)352 bool DeviceMapper::GetAvailableDevices(std::vector<DmBlockDevice>* devices) {
353 devices->clear();
354
355 // calculate the space needed to read a maximum of 256 targets, each with
356 // name with maximum length of 16 bytes
357 uint32_t payload_size = sizeof(struct dm_name_list);
358 // 128-bytes for the name
359 payload_size += DM_NAME_LEN;
360 // dm wants every device spec to be aligned at 8-byte boundary
361 payload_size = DM_ALIGN(payload_size);
362 payload_size *= kMaxPossibleDmDevices;
363 uint32_t data_size = sizeof(struct dm_ioctl) + payload_size;
364 auto buffer = std::unique_ptr<void, void (*)(void*)>(calloc(1, data_size), free);
365 if (buffer == nullptr) {
366 LOG(ERROR) << "failed to allocate memory";
367 return false;
368 }
369
370 // Sets appropriate data size and data_start to make sure we tell kernel
371 // about the total size of the buffer we are passing and where to start
372 // writing the list of targets.
373 struct dm_ioctl* io = reinterpret_cast<struct dm_ioctl*>(buffer.get());
374 InitIo(io);
375 io->data_size = data_size;
376 io->data_start = sizeof(*io);
377
378 if (ioctl(fd_, DM_LIST_DEVICES, io)) {
379 PLOG(ERROR) << "DM_LIST_DEVICES failed";
380 return false;
381 }
382
383 // If the provided buffer wasn't enough to list all devices any data
384 // beyond sizeof(*io) must not be read.
385 if (io->flags & DM_BUFFER_FULL_FLAG) {
386 LOG(INFO) << data_size << " is not enough memory to list all dm devices";
387 return false;
388 }
389
390 // if there are no devices created yet, return success with empty vector
391 if (io->data_size == sizeof(*io)) {
392 return true;
393 }
394
395 // Parse each device and add a new DmBlockDevice to the vector
396 // created from the kernel data.
397 uint32_t next = sizeof(*io);
398 data_size = io->data_size - next;
399 struct dm_name_list* dm_dev =
400 reinterpret_cast<struct dm_name_list*>(static_cast<char*>(buffer.get()) + next);
401
402 while (next && data_size) {
403 devices->emplace_back((dm_dev));
404 if (dm_dev->next == 0) {
405 break;
406 }
407 next += dm_dev->next;
408 data_size -= dm_dev->next;
409 dm_dev = reinterpret_cast<struct dm_name_list*>(static_cast<char*>(buffer.get()) + next);
410 }
411
412 return true;
413 }
414
415 // Accepts a device mapper device name (like system_a, vendor_b etc) and
416 // returns the path to it's device node (or symlink to the device node)
GetDmDevicePathByName(const std::string & name,std::string * path)417 bool DeviceMapper::GetDmDevicePathByName(const std::string& name, std::string* path) {
418 struct dm_ioctl io;
419 InitIo(&io, name);
420 if (ioctl(fd_, DM_DEV_STATUS, &io) < 0) {
421 PLOG(WARNING) << "DM_DEV_STATUS failed for " << name;
422 return false;
423 }
424
425 uint32_t dev_num = minor(io.dev);
426 *path = "/dev/block/dm-" + std::to_string(dev_num);
427 return true;
428 }
429
GetDeviceNumber(const std::string & name,dev_t * dev)430 bool DeviceMapper::GetDeviceNumber(const std::string& name, dev_t* dev) {
431 struct dm_ioctl io;
432 InitIo(&io, name);
433 if (ioctl(fd_, DM_DEV_STATUS, &io) < 0) {
434 PLOG(WARNING) << "DM_DEV_STATUS failed for " << name;
435 return false;
436 }
437 *dev = io.dev;
438 return true;
439 }
440
GetDeviceString(const std::string & name,std::string * dev)441 bool DeviceMapper::GetDeviceString(const std::string& name, std::string* dev) {
442 dev_t num;
443 if (!GetDeviceNumber(name, &num)) {
444 return false;
445 }
446 *dev = std::to_string(major(num)) + ":" + std::to_string(minor(num));
447 return true;
448 }
449
GetTableStatus(const std::string & name,std::vector<TargetInfo> * table)450 bool DeviceMapper::GetTableStatus(const std::string& name, std::vector<TargetInfo>* table) {
451 return GetTable(name, 0, table);
452 }
453
GetTableInfo(const std::string & name,std::vector<TargetInfo> * table)454 bool DeviceMapper::GetTableInfo(const std::string& name, std::vector<TargetInfo>* table) {
455 return GetTable(name, DM_STATUS_TABLE_FLAG, table);
456 }
457
458 // private methods of DeviceMapper
GetTable(const std::string & name,uint32_t flags,std::vector<TargetInfo> * table)459 bool DeviceMapper::GetTable(const std::string& name, uint32_t flags,
460 std::vector<TargetInfo>* table) {
461 std::vector<char> buffer;
462 struct dm_ioctl* io = nullptr;
463
464 for (buffer.resize(4096);; buffer.resize(buffer.size() * 2)) {
465 io = reinterpret_cast<struct dm_ioctl*>(&buffer[0]);
466
467 InitIo(io, name);
468 io->data_size = buffer.size();
469 io->data_start = sizeof(*io);
470 io->flags = flags;
471 if (ioctl(fd_, DM_TABLE_STATUS, io) < 0) {
472 PLOG(ERROR) << "DM_TABLE_STATUS failed for " << name;
473 return false;
474 }
475 if (!(io->flags & DM_BUFFER_FULL_FLAG)) break;
476 }
477
478 uint32_t cursor = io->data_start;
479 uint32_t data_end = std::min(io->data_size, uint32_t(buffer.size()));
480 for (uint32_t i = 0; i < io->target_count; i++) {
481 if (cursor + sizeof(struct dm_target_spec) > data_end) {
482 break;
483 }
484 // After each dm_target_spec is a status string. spec->next is an
485 // offset from |io->data_start|, and we clamp it to the size of our
486 // buffer.
487 struct dm_target_spec* spec = reinterpret_cast<struct dm_target_spec*>(&buffer[cursor]);
488 uint32_t data_offset = cursor + sizeof(dm_target_spec);
489 uint32_t next_cursor = std::min(io->data_start + spec->next, data_end);
490
491 std::string data;
492 if (next_cursor > data_offset) {
493 // Note: we use c_str() to eliminate any extra trailing 0s.
494 data = std::string(&buffer[data_offset], next_cursor - data_offset).c_str();
495 }
496 table->emplace_back(*spec, data);
497 cursor = next_cursor;
498 }
499 return true;
500 }
501
InitIo(struct dm_ioctl * io,const std::string & name) const502 void DeviceMapper::InitIo(struct dm_ioctl* io, const std::string& name) const {
503 CHECK(io != nullptr) << "nullptr passed to dm_ioctl initialization";
504 memset(io, 0, sizeof(*io));
505
506 io->version[0] = DM_VERSION0;
507 io->version[1] = DM_VERSION1;
508 io->version[2] = DM_VERSION2;
509 io->data_size = sizeof(*io);
510 io->data_start = 0;
511 if (!name.empty()) {
512 snprintf(io->name, sizeof(io->name), "%s", name.c_str());
513 }
514 }
515
GetTargetType(const struct dm_target_spec & spec)516 std::string DeviceMapper::GetTargetType(const struct dm_target_spec& spec) {
517 if (const void* p = memchr(spec.target_type, '\0', sizeof(spec.target_type))) {
518 ptrdiff_t length = reinterpret_cast<const char*>(p) - spec.target_type;
519 return std::string{spec.target_type, static_cast<size_t>(length)};
520 }
521 return std::string{spec.target_type, sizeof(spec.target_type)};
522 }
523
ExtractBlockDeviceName(const std::string & path,std::string * name)524 static bool ExtractBlockDeviceName(const std::string& path, std::string* name) {
525 static constexpr std::string_view kDevBlockPrefix("/dev/block/");
526 if (android::base::StartsWith(path, kDevBlockPrefix)) {
527 *name = path.substr(kDevBlockPrefix.length());
528 return true;
529 }
530 return false;
531 }
532
IsDmBlockDevice(const std::string & path)533 bool DeviceMapper::IsDmBlockDevice(const std::string& path) {
534 std::string name;
535 if (!ExtractBlockDeviceName(path, &name)) {
536 return false;
537 }
538 return android::base::StartsWith(name, "dm-");
539 }
540
GetDmDeviceNameByPath(const std::string & path)541 std::optional<std::string> DeviceMapper::GetDmDeviceNameByPath(const std::string& path) {
542 std::string name;
543 if (!ExtractBlockDeviceName(path, &name)) {
544 LOG(WARNING) << path << " is not a block device";
545 return std::nullopt;
546 }
547 if (!android::base::StartsWith(name, "dm-")) {
548 LOG(WARNING) << path << " is not a dm device";
549 return std::nullopt;
550 }
551 std::string dm_name_file = "/sys/block/" + name + "/dm/name";
552 std::string dm_name;
553 if (!android::base::ReadFileToString(dm_name_file, &dm_name)) {
554 PLOG(ERROR) << "Failed to read file " << dm_name_file;
555 return std::nullopt;
556 }
557 dm_name = android::base::Trim(dm_name);
558 return dm_name;
559 }
560
GetParentBlockDeviceByPath(const std::string & path)561 std::optional<std::string> DeviceMapper::GetParentBlockDeviceByPath(const std::string& path) {
562 std::string name;
563 if (!ExtractBlockDeviceName(path, &name)) {
564 LOG(WARNING) << path << " is not a block device";
565 return std::nullopt;
566 }
567 if (!android::base::StartsWith(name, "dm-")) {
568 // Reached bottom of the device mapper stack.
569 return std::nullopt;
570 }
571 auto slaves_dir = "/sys/block/" + name + "/slaves";
572 auto dir = std::unique_ptr<DIR, decltype(&closedir)>(opendir(slaves_dir.c_str()), closedir);
573 if (dir == nullptr) {
574 PLOG(ERROR) << "Failed to open: " << slaves_dir;
575 return std::nullopt;
576 }
577 std::string sub_device_name = "";
578 for (auto entry = readdir(dir.get()); entry; entry = readdir(dir.get())) {
579 if (entry->d_type != DT_LNK) continue;
580 if (!sub_device_name.empty()) {
581 LOG(ERROR) << "Too many slaves in " << slaves_dir;
582 return std::nullopt;
583 }
584 sub_device_name = entry->d_name;
585 }
586 if (sub_device_name.empty()) {
587 LOG(ERROR) << "No slaves in " << slaves_dir;
588 return std::nullopt;
589 }
590 return "/dev/block/" + sub_device_name;
591 }
592
IsOverflowSnapshot() const593 bool DeviceMapper::TargetInfo::IsOverflowSnapshot() const {
594 return spec.target_type == "snapshot"s && data == "Overflow"s;
595 }
596
597 } // namespace dm
598 } // namespace android
599