• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 #include <sys/utsname.h>
24 
25 #include <chrono>
26 #include <functional>
27 #include <string_view>
28 #include <thread>
29 
30 #include <android-base/file.h>
31 #include <android-base/logging.h>
32 #include <android-base/macros.h>
33 #include <android-base/properties.h>
34 #include <android-base/strings.h>
35 #include <uuid/uuid.h>
36 
37 #include "utility.h"
38 
39 #ifndef DM_DEFERRED_REMOVE
40 #define DM_DEFERRED_REMOVE (1 << 17)
41 #endif
42 
43 namespace android {
44 namespace dm {
45 
46 using namespace std::literals;
47 
DeviceMapper()48 DeviceMapper::DeviceMapper() : fd_(-1) {
49     fd_ = TEMP_FAILURE_RETRY(open("/dev/device-mapper", O_RDWR | O_CLOEXEC));
50     if (fd_ < 0) {
51         PLOG(ERROR) << "Failed to open device-mapper";
52     }
53 }
54 
Instance()55 DeviceMapper& DeviceMapper::Instance() {
56     static DeviceMapper instance;
57     return instance;
58 }
59 
60 // Creates a new device mapper device
CreateDevice(const std::string & name,const std::string & uuid)61 bool DeviceMapper::CreateDevice(const std::string& name, const std::string& uuid) {
62     if (name.empty()) {
63         LOG(ERROR) << "Unnamed device mapper device creation is not supported";
64         return false;
65     }
66     if (name.size() >= DM_NAME_LEN) {
67         LOG(ERROR) << "[" << name << "] is too long to be device mapper name";
68         return false;
69     }
70 
71     struct dm_ioctl io;
72     InitIo(&io, name);
73     if (!uuid.empty()) {
74         snprintf(io.uuid, sizeof(io.uuid), "%s", uuid.c_str());
75     }
76 
77     if (ioctl(fd_, DM_DEV_CREATE, &io)) {
78         PLOG(ERROR) << "DM_DEV_CREATE failed for [" << name << "]";
79         return false;
80     }
81 
82     // Check to make sure the newly created device doesn't already have targets
83     // added or opened by someone
84     CHECK(io.target_count == 0) << "Unexpected targets for newly created [" << name << "] device";
85     CHECK(io.open_count == 0) << "Unexpected opens for newly created [" << name << "] device";
86 
87     // Creates a new device mapper device with the name passed in
88     return true;
89 }
90 
DeleteDeviceIfExists(const std::string & name,const std::chrono::milliseconds & timeout_ms)91 bool DeviceMapper::DeleteDeviceIfExists(const std::string& name,
92                                         const std::chrono::milliseconds& timeout_ms) {
93     if (GetState(name) == DmDeviceState::INVALID) {
94         return true;
95     }
96     return DeleteDevice(name, timeout_ms);
97 }
98 
DeleteDeviceIfExists(const std::string & name)99 bool DeviceMapper::DeleteDeviceIfExists(const std::string& name) {
100     return DeleteDeviceIfExists(name, 0ms);
101 }
102 
DeleteDevice(const std::string & name,const std::chrono::milliseconds & timeout_ms)103 bool DeviceMapper::DeleteDevice(const std::string& name,
104                                 const std::chrono::milliseconds& timeout_ms) {
105     std::string unique_path;
106     if (!GetDeviceUniquePath(name, &unique_path)) {
107         LOG(ERROR) << "Failed to get unique path for device " << name;
108     }
109     struct dm_ioctl io;
110     InitIo(&io, name);
111 
112     if (ioctl(fd_, DM_DEV_REMOVE, &io)) {
113         PLOG(ERROR) << "DM_DEV_REMOVE failed for [" << name << "]";
114         return false;
115     }
116 
117     // Check to make sure appropriate uevent is generated so ueventd will
118     // do the right thing and remove the corresponding device node and symlinks.
119     if ((io.flags & DM_UEVENT_GENERATED_FLAG) == 0) {
120         LOG(ERROR) << "Didn't generate uevent for [" << name << "] removal";
121         return false;
122     }
123 
124     if (timeout_ms <= std::chrono::milliseconds::zero()) {
125         return true;
126     }
127     if (unique_path.empty()) {
128         return false;
129     }
130     if (!WaitForFileDeleted(unique_path, timeout_ms)) {
131         LOG(ERROR) << "Failed waiting for " << unique_path << " to be deleted";
132         return false;
133     }
134     return true;
135 }
136 
DeleteDevice(const std::string & name)137 bool DeviceMapper::DeleteDevice(const std::string& name) {
138     return DeleteDevice(name, 0ms);
139 }
140 
DeleteDeviceDeferred(const std::string & name)141 bool DeviceMapper::DeleteDeviceDeferred(const std::string& name) {
142     struct dm_ioctl io;
143     InitIo(&io, name);
144 
145     io.flags |= DM_DEFERRED_REMOVE;
146     if (ioctl(fd_, DM_DEV_REMOVE, &io)) {
147         PLOG(ERROR) << "DM_DEV_REMOVE with DM_DEFERRED_REMOVE failed for [" << name << "]";
148         return false;
149     }
150     return true;
151 }
152 
DeleteDeviceIfExistsDeferred(const std::string & name)153 bool DeviceMapper::DeleteDeviceIfExistsDeferred(const std::string& name) {
154     if (GetState(name) == DmDeviceState::INVALID) {
155         return true;
156     }
157     return DeleteDeviceDeferred(name);
158 }
159 
GenerateUuid()160 static std::string GenerateUuid() {
161     uuid_t uuid_bytes;
162     uuid_generate(uuid_bytes);
163 
164     char uuid_chars[37] = {};
165     uuid_unparse_lower(uuid_bytes, uuid_chars);
166 
167     return std::string{uuid_chars};
168 }
169 
IsRecovery()170 static bool IsRecovery() {
171     return access("/system/bin/recovery", F_OK) == 0;
172 }
173 
CreateEmptyDevice(const std::string & name)174 bool DeviceMapper::CreateEmptyDevice(const std::string& name) {
175     std::string uuid = GenerateUuid();
176     return CreateDevice(name, uuid);
177 }
178 
WaitForDevice(const std::string & name,const std::chrono::milliseconds & timeout_ms,std::string * path)179 bool DeviceMapper::WaitForDevice(const std::string& name,
180                                  const std::chrono::milliseconds& timeout_ms, std::string* path) {
181     // We use the unique path for testing whether the device is ready. After
182     // that, it's safe to use the dm-N path which is compatible with callers
183     // that expect it to be formatted as such.
184     std::string unique_path;
185     if (!GetDeviceUniquePath(name, &unique_path) || !GetDmDevicePathByName(name, path)) {
186         DeleteDevice(name);
187         return false;
188     }
189 
190     if (timeout_ms <= std::chrono::milliseconds::zero()) {
191         return true;
192     }
193 
194     if (IsRecovery()) {
195         bool non_ab_device = android::base::GetProperty("ro.build.ab_update", "").empty();
196         int sdk = android::base::GetIntProperty("ro.build.version.sdk", 0);
197         if (non_ab_device && sdk && sdk <= 29) {
198             LOG(INFO) << "Detected ueventd incompatibility, reverting to legacy libdm behavior.";
199             unique_path = *path;
200         }
201     }
202 
203     if (!WaitForFile(unique_path, timeout_ms)) {
204         LOG(ERROR) << "Failed waiting for device path: " << unique_path;
205         DeleteDevice(name);
206         return false;
207     }
208     return true;
209 }
210 
CreateDevice(const std::string & name,const DmTable & table,std::string * path,const std::chrono::milliseconds & timeout_ms)211 bool DeviceMapper::CreateDevice(const std::string& name, const DmTable& table, std::string* path,
212                                 const std::chrono::milliseconds& timeout_ms) {
213     if (!CreateEmptyDevice(name)) {
214         return false;
215     }
216 
217     if (!LoadTableAndActivate(name, table)) {
218         DeleteDevice(name);
219         return false;
220     }
221 
222     if (!WaitForDevice(name, timeout_ms, path)) {
223         DeleteDevice(name);
224         return false;
225     }
226 
227     return true;
228 }
229 
GetDeviceUniquePath(const std::string & name,std::string * path)230 bool DeviceMapper::GetDeviceUniquePath(const std::string& name, std::string* path) {
231     struct dm_ioctl io;
232     InitIo(&io, name);
233     if (ioctl(fd_, DM_DEV_STATUS, &io) < 0) {
234         PLOG(ERROR) << "Failed to get device path: " << name;
235         return false;
236     }
237 
238     if (io.uuid[0] == '\0') {
239         LOG(ERROR) << "Device does not have a unique path: " << name;
240         return false;
241     }
242     *path = "/dev/block/mapper/by-uuid/"s + io.uuid;
243     return true;
244 }
245 
GetDeviceNameAndUuid(dev_t dev,std::string * name,std::string * uuid)246 bool DeviceMapper::GetDeviceNameAndUuid(dev_t dev, std::string* name, std::string* uuid) {
247     struct dm_ioctl io;
248     InitIo(&io, {});
249     io.dev = dev;
250 
251     if (ioctl(fd_, DM_DEV_STATUS, &io) < 0) {
252         PLOG(ERROR) << "Failed to find device dev: " << major(dev) << ":" << minor(dev);
253         return false;
254     }
255 
256     if (name) {
257         *name = io.name;
258     }
259     if (uuid) {
260         *uuid = io.uuid;
261     }
262     return true;
263 }
264 
GetDetailedInfo(const std::string & name) const265 std::optional<DeviceMapper::Info> DeviceMapper::GetDetailedInfo(const std::string& name) const {
266     struct dm_ioctl io;
267     InitIo(&io, name);
268     if (ioctl(fd_, DM_DEV_STATUS, &io) < 0) {
269         return std::nullopt;
270     }
271     return Info(io.flags);
272 }
273 
GetState(const std::string & name) const274 DmDeviceState DeviceMapper::GetState(const std::string& name) const {
275     struct dm_ioctl io;
276     InitIo(&io, name);
277     if (ioctl(fd_, DM_DEV_STATUS, &io) < 0) {
278         return DmDeviceState::INVALID;
279     }
280     if ((io.flags & DM_ACTIVE_PRESENT_FLAG) && !(io.flags & DM_SUSPEND_FLAG)) {
281         return DmDeviceState::ACTIVE;
282     }
283     return DmDeviceState::SUSPENDED;
284 }
285 
ChangeState(const std::string & name,DmDeviceState state)286 bool DeviceMapper::ChangeState(const std::string& name, DmDeviceState state) {
287     if (state != DmDeviceState::SUSPENDED && state != DmDeviceState::ACTIVE) {
288         return false;
289     }
290 
291     struct dm_ioctl io;
292     InitIo(&io, name);
293 
294     if (state == DmDeviceState::SUSPENDED) io.flags = DM_SUSPEND_FLAG;
295 
296     if (ioctl(fd_, DM_DEV_SUSPEND, &io) < 0) {
297         PLOG(ERROR) << "DM_DEV_SUSPEND "
298                     << (state == DmDeviceState::SUSPENDED ? "suspend" : "resume") << " failed";
299         return false;
300     }
301     return true;
302 }
303 
CreateDevice(const std::string & name,const DmTable & table)304 bool DeviceMapper::CreateDevice(const std::string& name, const DmTable& table) {
305     std::string ignore_path;
306     if (!CreateDevice(name, table, &ignore_path, 0ms)) {
307         return false;
308     }
309     return true;
310 }
311 
LoadTable(const std::string & name,const DmTable & table)312 bool DeviceMapper::LoadTable(const std::string& name, const DmTable& table) {
313     std::string ioctl_buffer(sizeof(struct dm_ioctl), 0);
314     ioctl_buffer += table.Serialize();
315 
316     struct dm_ioctl* io = reinterpret_cast<struct dm_ioctl*>(&ioctl_buffer[0]);
317     InitIo(io, name);
318     io->data_size = ioctl_buffer.size();
319     io->data_start = sizeof(struct dm_ioctl);
320     io->target_count = static_cast<uint32_t>(table.num_targets());
321     if (table.readonly()) {
322         io->flags |= DM_READONLY_FLAG;
323     }
324     if (ioctl(fd_, DM_TABLE_LOAD, io)) {
325         PLOG(ERROR) << "DM_TABLE_LOAD failed";
326         return false;
327     }
328     return true;
329 }
330 
LoadTableAndActivate(const std::string & name,const DmTable & table)331 bool DeviceMapper::LoadTableAndActivate(const std::string& name, const DmTable& table) {
332     if (!LoadTable(name, table)) {
333         return false;
334     }
335 
336     struct dm_ioctl io;
337     InitIo(&io, name);
338     if (ioctl(fd_, DM_DEV_SUSPEND, &io)) {
339         PLOG(ERROR) << "DM_TABLE_SUSPEND resume failed";
340         return false;
341     }
342     return true;
343 }
344 
345 // Reads all the available device mapper targets and their corresponding
346 // versions from the kernel and returns in a vector
GetAvailableTargets(std::vector<DmTargetTypeInfo> * targets)347 bool DeviceMapper::GetAvailableTargets(std::vector<DmTargetTypeInfo>* targets) {
348     targets->clear();
349 
350     // calculate the space needed to read a maximum of kMaxPossibleDmTargets
351     uint32_t payload_size = sizeof(struct dm_target_versions);
352     payload_size += DM_MAX_TYPE_NAME;
353     // device mapper wants every target spec to be aligned at 8-byte boundary
354     payload_size = DM_ALIGN(payload_size);
355     payload_size *= kMaxPossibleDmTargets;
356 
357     uint32_t data_size = sizeof(struct dm_ioctl) + payload_size;
358     auto buffer = std::unique_ptr<void, void (*)(void*)>(calloc(1, data_size), free);
359     if (buffer == nullptr) {
360         LOG(ERROR) << "failed to allocate memory";
361         return false;
362     }
363 
364     // Sets appropriate data size and data_start to make sure we tell kernel
365     // about the total size of the buffer we are passing and where to start
366     // writing the list of targets.
367     struct dm_ioctl* io = reinterpret_cast<struct dm_ioctl*>(buffer.get());
368     InitIo(io);
369     io->data_size = data_size;
370     io->data_start = sizeof(*io);
371 
372     if (ioctl(fd_, DM_LIST_VERSIONS, io)) {
373         PLOG(ERROR) << "DM_LIST_VERSIONS failed";
374         return false;
375     }
376 
377     // If the provided buffer wasn't enough to list all targets, note that
378     // any data beyond sizeof(*io) must not be read in this case
379     if (io->flags & DM_BUFFER_FULL_FLAG) {
380         LOG(INFO) << data_size << " is not enough memory to list all dm targets";
381         return false;
382     }
383 
384     // if there are no targets registered, return success with empty vector
385     if (io->data_size == sizeof(*io)) {
386         return true;
387     }
388 
389     // Parse each target and list the name and version
390     // TODO(b/110035986): Templatize this
391     uint32_t next = sizeof(*io);
392     data_size = io->data_size - next;
393     struct dm_target_versions* vers =
394             reinterpret_cast<struct dm_target_versions*>(static_cast<char*>(buffer.get()) + next);
395     while (next && data_size) {
396         targets->emplace_back(vers);
397         if (vers->next == 0) {
398             break;
399         }
400         next += vers->next;
401         data_size -= vers->next;
402         vers = reinterpret_cast<struct dm_target_versions*>(static_cast<char*>(buffer.get()) +
403                                                             next);
404     }
405 
406     return true;
407 }
408 
GetTargetByName(const std::string & name,DmTargetTypeInfo * info)409 bool DeviceMapper::GetTargetByName(const std::string& name, DmTargetTypeInfo* info) {
410     std::vector<DmTargetTypeInfo> targets;
411     if (!GetAvailableTargets(&targets)) {
412         return false;
413     }
414     for (const auto& target : targets) {
415         if (target.name() == name) {
416             if (info) *info = target;
417             return true;
418         }
419     }
420     return false;
421 }
422 
GetAvailableDevices(std::vector<DmBlockDevice> * devices)423 bool DeviceMapper::GetAvailableDevices(std::vector<DmBlockDevice>* devices) {
424     devices->clear();
425 
426     // calculate the space needed to read a maximum of 256 targets, each with
427     // name with maximum length of 16 bytes
428     uint32_t payload_size = sizeof(struct dm_name_list);
429     // 128-bytes for the name
430     payload_size += DM_NAME_LEN;
431     // dm wants every device spec to be aligned at 8-byte boundary
432     payload_size = DM_ALIGN(payload_size);
433     payload_size *= kMaxPossibleDmDevices;
434     uint32_t data_size = sizeof(struct dm_ioctl) + payload_size;
435     auto buffer = std::unique_ptr<void, void (*)(void*)>(calloc(1, data_size), free);
436     if (buffer == nullptr) {
437         LOG(ERROR) << "failed to allocate memory";
438         return false;
439     }
440 
441     // Sets appropriate data size and data_start to make sure we tell kernel
442     // about the total size of the buffer we are passing and where to start
443     // writing the list of targets.
444     struct dm_ioctl* io = reinterpret_cast<struct dm_ioctl*>(buffer.get());
445     InitIo(io);
446     io->data_size = data_size;
447     io->data_start = sizeof(*io);
448 
449     if (ioctl(fd_, DM_LIST_DEVICES, io)) {
450         PLOG(ERROR) << "DM_LIST_DEVICES failed";
451         return false;
452     }
453 
454     // If the provided buffer wasn't enough to list all devices any data
455     // beyond sizeof(*io) must not be read.
456     if (io->flags & DM_BUFFER_FULL_FLAG) {
457         LOG(INFO) << data_size << " is not enough memory to list all dm devices";
458         return false;
459     }
460 
461     // if there are no devices created yet, return success with empty vector
462     if (io->data_size == sizeof(*io)) {
463         return true;
464     }
465 
466     // Parse each device and add a new DmBlockDevice to the vector
467     // created from the kernel data.
468     uint32_t next = sizeof(*io);
469     data_size = io->data_size - next;
470     struct dm_name_list* dm_dev =
471             reinterpret_cast<struct dm_name_list*>(static_cast<char*>(buffer.get()) + next);
472 
473     while (next && data_size) {
474         devices->emplace_back((dm_dev));
475         if (dm_dev->next == 0) {
476             break;
477         }
478         next += dm_dev->next;
479         data_size -= dm_dev->next;
480         dm_dev = reinterpret_cast<struct dm_name_list*>(static_cast<char*>(buffer.get()) + next);
481     }
482 
483     return true;
484 }
485 
486 // Accepts a device mapper device name (like system_a, vendor_b etc) and
487 // returns the path to it's device node (or symlink to the device node)
GetDmDevicePathByName(const std::string & name,std::string * path)488 bool DeviceMapper::GetDmDevicePathByName(const std::string& name, std::string* path) {
489     struct dm_ioctl io;
490     InitIo(&io, name);
491     if (ioctl(fd_, DM_DEV_STATUS, &io) < 0) {
492         PLOG(WARNING) << "DM_DEV_STATUS failed for " << name;
493         return false;
494     }
495 
496     uint32_t dev_num = minor(io.dev);
497     *path = "/dev/block/dm-" + std::to_string(dev_num);
498     return true;
499 }
500 
501 // Accepts a device mapper device name (like system_a, vendor_b etc) and
502 // returns its UUID.
GetDmDeviceUuidByName(const std::string & name,std::string * uuid)503 bool DeviceMapper::GetDmDeviceUuidByName(const std::string& name, std::string* uuid) {
504     struct dm_ioctl io;
505     InitIo(&io, name);
506     if (ioctl(fd_, DM_DEV_STATUS, &io) < 0) {
507         PLOG(WARNING) << "DM_DEV_STATUS failed for " << name;
508         return false;
509     }
510 
511     *uuid = std::string(io.uuid);
512     return true;
513 }
514 
GetDeviceNumber(const std::string & name,dev_t * dev)515 bool DeviceMapper::GetDeviceNumber(const std::string& name, dev_t* dev) {
516     struct dm_ioctl io;
517     InitIo(&io, name);
518     if (ioctl(fd_, DM_DEV_STATUS, &io) < 0) {
519         PLOG(WARNING) << "DM_DEV_STATUS failed for " << name;
520         return false;
521     }
522     *dev = io.dev;
523     return true;
524 }
525 
GetDeviceString(const std::string & name,std::string * dev)526 bool DeviceMapper::GetDeviceString(const std::string& name, std::string* dev) {
527     dev_t num;
528     if (!GetDeviceNumber(name, &num)) {
529         return false;
530     }
531     *dev = std::to_string(major(num)) + ":" + std::to_string(minor(num));
532     return true;
533 }
534 
GetTableStatus(const std::string & name,std::vector<TargetInfo> * table)535 bool DeviceMapper::GetTableStatus(const std::string& name, std::vector<TargetInfo>* table) {
536     return GetTable(name, 0, table);
537 }
538 
GetTableInfo(const std::string & name,std::vector<TargetInfo> * table)539 bool DeviceMapper::GetTableInfo(const std::string& name, std::vector<TargetInfo>* table) {
540     return GetTable(name, DM_STATUS_TABLE_FLAG, table);
541 }
542 
543 // private methods of DeviceMapper
GetTable(const std::string & name,uint32_t flags,std::vector<TargetInfo> * table)544 bool DeviceMapper::GetTable(const std::string& name, uint32_t flags,
545                             std::vector<TargetInfo>* table) {
546     std::vector<char> buffer;
547     struct dm_ioctl* io = nullptr;
548 
549     for (buffer.resize(4096);; buffer.resize(buffer.size() * 2)) {
550         io = reinterpret_cast<struct dm_ioctl*>(&buffer[0]);
551 
552         InitIo(io, name);
553         io->data_size = buffer.size();
554         io->data_start = sizeof(*io);
555         io->flags = flags;
556         if (ioctl(fd_, DM_TABLE_STATUS, io) < 0) {
557             PLOG(ERROR) << "DM_TABLE_STATUS failed for " << name;
558             return false;
559         }
560         if (!(io->flags & DM_BUFFER_FULL_FLAG)) break;
561     }
562 
563     uint32_t cursor = io->data_start;
564     uint32_t data_end = std::min(io->data_size, uint32_t(buffer.size()));
565     for (uint32_t i = 0; i < io->target_count; i++) {
566         if (cursor + sizeof(struct dm_target_spec) > data_end) {
567             break;
568         }
569         // After each dm_target_spec is a status string. spec->next is an
570         // offset from |io->data_start|, and we clamp it to the size of our
571         // buffer.
572         struct dm_target_spec* spec = reinterpret_cast<struct dm_target_spec*>(&buffer[cursor]);
573         uint32_t data_offset = cursor + sizeof(dm_target_spec);
574         uint32_t next_cursor = std::min(io->data_start + spec->next, data_end);
575 
576         std::string data;
577         if (next_cursor > data_offset) {
578             // Note: we use c_str() to eliminate any extra trailing 0s.
579             data = std::string(&buffer[data_offset], next_cursor - data_offset).c_str();
580         }
581         table->emplace_back(*spec, data);
582         cursor = next_cursor;
583     }
584     return true;
585 }
586 
InitIo(struct dm_ioctl * io,const std::string & name) const587 void DeviceMapper::InitIo(struct dm_ioctl* io, const std::string& name) const {
588     CHECK(io != nullptr) << "nullptr passed to dm_ioctl initialization";
589     memset(io, 0, sizeof(*io));
590 
591     io->version[0] = DM_VERSION0;
592     io->version[1] = DM_VERSION1;
593     io->version[2] = DM_VERSION2;
594     io->data_size = sizeof(*io);
595     io->data_start = 0;
596     if (!name.empty()) {
597         snprintf(io->name, sizeof(io->name), "%s", name.c_str());
598     }
599 }
600 
GetTargetType(const struct dm_target_spec & spec)601 std::string DeviceMapper::GetTargetType(const struct dm_target_spec& spec) {
602     if (const void* p = memchr(spec.target_type, '\0', sizeof(spec.target_type))) {
603         ptrdiff_t length = reinterpret_cast<const char*>(p) - spec.target_type;
604         return std::string{spec.target_type, static_cast<size_t>(length)};
605     }
606     return std::string{spec.target_type, sizeof(spec.target_type)};
607 }
608 
ExtractBlockDeviceName(const std::string & path)609 std::optional<std::string> ExtractBlockDeviceName(const std::string& path) {
610     static constexpr std::string_view kDevBlockPrefix("/dev/block/");
611     if (android::base::StartsWith(path, kDevBlockPrefix)) {
612         return path.substr(kDevBlockPrefix.length());
613     }
614     return {};
615 }
616 
IsDmBlockDevice(const std::string & path)617 bool DeviceMapper::IsDmBlockDevice(const std::string& path) {
618     std::optional<std::string> name = ExtractBlockDeviceName(path);
619     return name && android::base::StartsWith(*name, "dm-");
620 }
621 
GetDmDeviceNameByPath(const std::string & path)622 std::optional<std::string> DeviceMapper::GetDmDeviceNameByPath(const std::string& path) {
623     std::optional<std::string> name = ExtractBlockDeviceName(path);
624     if (!name) {
625         LOG(WARNING) << path << " is not a block device";
626         return std::nullopt;
627     }
628     if (!android::base::StartsWith(*name, "dm-")) {
629         LOG(WARNING) << path << " is not a dm device";
630         return std::nullopt;
631     }
632     std::string dm_name_file = "/sys/block/" + *name + "/dm/name";
633     std::string dm_name;
634     if (!android::base::ReadFileToString(dm_name_file, &dm_name)) {
635         PLOG(ERROR) << "Failed to read file " << dm_name_file;
636         return std::nullopt;
637     }
638     dm_name = android::base::Trim(dm_name);
639     return dm_name;
640 }
641 
GetParentBlockDeviceByPath(const std::string & path)642 std::optional<std::string> DeviceMapper::GetParentBlockDeviceByPath(const std::string& path) {
643     std::optional<std::string> name = ExtractBlockDeviceName(path);
644     if (!name) {
645         LOG(WARNING) << path << " is not a block device";
646         return std::nullopt;
647     }
648     if (!android::base::StartsWith(*name, "dm-")) {
649         // Reached bottom of the device mapper stack.
650         return std::nullopt;
651     }
652     auto slaves_dir = "/sys/block/" + *name + "/slaves";
653     auto dir = std::unique_ptr<DIR, decltype(&closedir)>(opendir(slaves_dir.c_str()), closedir);
654     if (dir == nullptr) {
655         PLOG(ERROR) << "Failed to open: " << slaves_dir;
656         return std::nullopt;
657     }
658     std::string sub_device_name = "";
659     for (auto entry = readdir(dir.get()); entry; entry = readdir(dir.get())) {
660         if (entry->d_type != DT_LNK) continue;
661         if (!sub_device_name.empty()) {
662             LOG(ERROR) << "Too many slaves in " << slaves_dir;
663             return std::nullopt;
664         }
665         sub_device_name = entry->d_name;
666     }
667     if (sub_device_name.empty()) {
668         LOG(ERROR) << "No slaves in " << slaves_dir;
669         return std::nullopt;
670     }
671     return "/dev/block/" + sub_device_name;
672 }
673 
IsOverflowSnapshot() const674 bool DeviceMapper::TargetInfo::IsOverflowSnapshot() const {
675     return spec.target_type == "snapshot"s && data == "Overflow"s;
676 }
677 
678 // Find directories in format of "/sys/block/dm-X".
DmNameFilter(const dirent * de)679 static int DmNameFilter(const dirent* de) {
680     if (android::base::StartsWith(de->d_name, "dm-")) {
681         return 1;
682     }
683     return 0;
684 }
685 
FindDmPartitions()686 std::map<std::string, std::string> DeviceMapper::FindDmPartitions() {
687     static constexpr auto DM_PATH_PREFIX = "/sys/block/";
688     dirent** namelist;
689     int n = scandir(DM_PATH_PREFIX, &namelist, DmNameFilter, alphasort);
690     if (n == -1) {
691         PLOG(ERROR) << "Failed to scan dir " << DM_PATH_PREFIX;
692         return {};
693     }
694     if (n == 0) {
695         LOG(ERROR) << "No dm block device found.";
696         free(namelist);
697         return {};
698     }
699 
700     static constexpr auto DM_PATH_SUFFIX = "/dm/name";
701     static constexpr auto DEV_PATH = "/dev/block/";
702     std::map<std::string, std::string> dm_block_devices;
703     while (n--) {
704         std::string path = DM_PATH_PREFIX + std::string(namelist[n]->d_name) + DM_PATH_SUFFIX;
705         std::string content;
706         if (!android::base::ReadFileToString(path, &content)) {
707             PLOG(WARNING) << "Failed to read " << path;
708         } else {
709             std::string dm_block_name = android::base::Trim(content);
710             // AVB is using 'vroot' for the root block device but we're expecting 'system'.
711             if (dm_block_name == "vroot") {
712                 dm_block_name = "system";
713             } else if (android::base::EndsWith(dm_block_name, "-verity")) {
714                 auto npos = dm_block_name.rfind("-verity");
715                 dm_block_name = dm_block_name.substr(0, npos);
716             } else if (!android::base::GetProperty("ro.boot.avb_version", "").empty()) {
717                 // Verified Boot 1.0 doesn't add a -verity suffix. On AVB 2 devices,
718                 // if DAP is enabled, then a -verity suffix must be used to
719                 // differentiate between dm-linear and dm-verity devices. If we get
720                 // here, we're AVB 2 and looking at a non-verity partition.
721                 free(namelist[n]);
722                 continue;
723             }
724 
725             dm_block_devices.emplace(dm_block_name, DEV_PATH + std::string(namelist[n]->d_name));
726         }
727         free(namelist[n]);
728     }
729     free(namelist);
730 
731     return dm_block_devices;
732 }
733 
CreatePlaceholderDevice(const std::string & name)734 bool DeviceMapper::CreatePlaceholderDevice(const std::string& name) {
735     if (!CreateEmptyDevice(name)) {
736         return false;
737     }
738 
739     struct utsname uts;
740     unsigned int major, minor;
741     if (uname(&uts) != 0 || sscanf(uts.release, "%u.%u", &major, &minor) != 2) {
742         LOG(ERROR) << "Could not parse the kernel version from uname";
743         return true;
744     }
745 
746     // On Linux 5.15+, there is no uevent until DM_TABLE_LOAD.
747     if (major > 5 || (major == 5 && minor >= 15)) {
748         DmTable table;
749         table.Emplace<DmTargetError>(0, 1);
750         if (!LoadTable(name, table)) {
751             return false;
752         }
753     }
754     return true;
755 }
756 
757 }  // namespace dm
758 }  // namespace android
759