• 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 <sys/ioctl.h>
20 #include <sys/sysmacros.h>
21 #include <sys/types.h>
22 
23 #include <android-base/logging.h>
24 #include <android-base/macros.h>
25 
26 namespace android {
27 namespace dm {
28 
DeviceMapper()29 DeviceMapper::DeviceMapper() : fd_(-1) {
30     fd_ = TEMP_FAILURE_RETRY(open("/dev/device-mapper", O_RDWR | O_CLOEXEC));
31     if (fd_ < 0) {
32         PLOG(ERROR) << "Failed to open device-mapper";
33     }
34 }
35 
Instance()36 DeviceMapper& DeviceMapper::Instance() {
37     static DeviceMapper instance;
38     return instance;
39 }
40 // Creates a new device mapper device
CreateDevice(const std::string & name)41 bool DeviceMapper::CreateDevice(const std::string& name) {
42     if (name.empty()) {
43         LOG(ERROR) << "Unnamed device mapper device creation is not supported";
44         return false;
45     }
46 
47     if (name.size() >= DM_NAME_LEN) {
48         LOG(ERROR) << "[" << name << "] is too long to be device mapper name";
49         return false;
50     }
51 
52     struct dm_ioctl io;
53     InitIo(&io, name);
54 
55     if (ioctl(fd_, DM_DEV_CREATE, &io)) {
56         PLOG(ERROR) << "DM_DEV_CREATE failed for [" << name << "]";
57         return false;
58     }
59 
60     // Check to make sure the newly created device doesn't already have targets
61     // added or opened by someone
62     CHECK(io.target_count == 0) << "Unexpected targets for newly created [" << name << "] device";
63     CHECK(io.open_count == 0) << "Unexpected opens for newly created [" << name << "] device";
64 
65     // Creates a new device mapper device with the name passed in
66     return true;
67 }
68 
DeleteDevice(const std::string & name)69 bool DeviceMapper::DeleteDevice(const std::string& name) {
70     if (name.empty()) {
71         LOG(ERROR) << "Unnamed device mapper device creation is not supported";
72         return false;
73     }
74 
75     if (name.size() >= DM_NAME_LEN) {
76         LOG(ERROR) << "[" << name << "] is too long to be device mapper name";
77         return false;
78     }
79 
80     struct dm_ioctl io;
81     InitIo(&io, name);
82 
83     if (ioctl(fd_, DM_DEV_REMOVE, &io)) {
84         PLOG(ERROR) << "DM_DEV_REMOVE failed for [" << name << "]";
85         return false;
86     }
87 
88     // Check to make sure appropriate uevent is generated so ueventd will
89     // do the right thing and remove the corresponding device node and symlinks.
90     CHECK(io.flags & DM_UEVENT_GENERATED_FLAG)
91             << "Didn't generate uevent for [" << name << "] removal";
92 
93     return true;
94 }
95 
table(const std::string &) const96 const std::unique_ptr<DmTable> DeviceMapper::table(const std::string& /* name */) const {
97     // TODO(b/110035986): Return the table, as read from the kernel instead
98     return nullptr;
99 }
100 
GetState(const std::string & name) const101 DmDeviceState DeviceMapper::GetState(const std::string& name) const {
102     struct dm_ioctl io;
103     InitIo(&io, name);
104     if (ioctl(fd_, DM_DEV_STATUS, &io) < 0) {
105         return DmDeviceState::INVALID;
106     }
107     if ((io.flags & DM_ACTIVE_PRESENT_FLAG) && !(io.flags & DM_SUSPEND_FLAG)) {
108         return DmDeviceState::ACTIVE;
109     }
110     return DmDeviceState::SUSPENDED;
111 }
112 
CreateDevice(const std::string & name,const DmTable & table)113 bool DeviceMapper::CreateDevice(const std::string& name, const DmTable& table) {
114     if (!CreateDevice(name)) {
115         return false;
116     }
117     if (!LoadTableAndActivate(name, table)) {
118         DeleteDevice(name);
119         return false;
120     }
121     return true;
122 }
123 
LoadTableAndActivate(const std::string & name,const DmTable & table)124 bool DeviceMapper::LoadTableAndActivate(const std::string& name, const DmTable& table) {
125     std::string ioctl_buffer(sizeof(struct dm_ioctl), 0);
126     ioctl_buffer += table.Serialize();
127 
128     struct dm_ioctl* io = reinterpret_cast<struct dm_ioctl*>(&ioctl_buffer[0]);
129     InitIo(io, name);
130     io->data_size = ioctl_buffer.size();
131     io->data_start = sizeof(struct dm_ioctl);
132     io->target_count = static_cast<uint32_t>(table.num_targets());
133     if (table.readonly()) {
134         io->flags |= DM_READONLY_FLAG;
135     }
136     if (ioctl(fd_, DM_TABLE_LOAD, io)) {
137         PLOG(ERROR) << "DM_TABLE_LOAD failed";
138         return false;
139     }
140 
141     InitIo(io, name);
142     if (ioctl(fd_, DM_DEV_SUSPEND, io)) {
143         PLOG(ERROR) << "DM_TABLE_SUSPEND resume failed";
144         return false;
145     }
146     return true;
147 }
148 
149 // Reads all the available device mapper targets and their corresponding
150 // versions from the kernel and returns in a vector
GetAvailableTargets(std::vector<DmTargetTypeInfo> * targets)151 bool DeviceMapper::GetAvailableTargets(std::vector<DmTargetTypeInfo>* targets) {
152     targets->clear();
153 
154     // calculate the space needed to read a maximum of kMaxPossibleDmTargets
155     uint32_t payload_size = sizeof(struct dm_target_versions);
156     payload_size += DM_MAX_TYPE_NAME;
157     // device mapper wants every target spec to be aligned at 8-byte boundary
158     payload_size = DM_ALIGN(payload_size);
159     payload_size *= kMaxPossibleDmTargets;
160 
161     uint32_t data_size = sizeof(struct dm_ioctl) + payload_size;
162     auto buffer = std::unique_ptr<void, void (*)(void*)>(calloc(1, data_size), free);
163     if (buffer == nullptr) {
164         LOG(ERROR) << "failed to allocate memory";
165         return false;
166     }
167 
168     // Sets appropriate data size and data_start to make sure we tell kernel
169     // about the total size of the buffer we are passing and where to start
170     // writing the list of targets.
171     struct dm_ioctl* io = reinterpret_cast<struct dm_ioctl*>(buffer.get());
172     InitIo(io);
173     io->data_size = data_size;
174     io->data_start = sizeof(*io);
175 
176     if (ioctl(fd_, DM_LIST_VERSIONS, io)) {
177         PLOG(ERROR) << "DM_LIST_VERSIONS failed";
178         return false;
179     }
180 
181     // If the provided buffer wasn't enough to list all targets, note that
182     // any data beyond sizeof(*io) must not be read in this case
183     if (io->flags & DM_BUFFER_FULL_FLAG) {
184         LOG(INFO) << data_size << " is not enough memory to list all dm targets";
185         return false;
186     }
187 
188     // if there are no targets registered, return success with empty vector
189     if (io->data_size == sizeof(*io)) {
190         return true;
191     }
192 
193     // Parse each target and list the name and version
194     // TODO(b/110035986): Templatize this
195     uint32_t next = sizeof(*io);
196     data_size = io->data_size - next;
197     struct dm_target_versions* vers =
198             reinterpret_cast<struct dm_target_versions*>(static_cast<char*>(buffer.get()) + next);
199     while (next && data_size) {
200         targets->emplace_back(vers);
201         if (vers->next == 0) {
202             break;
203         }
204         next += vers->next;
205         data_size -= vers->next;
206         vers = reinterpret_cast<struct dm_target_versions*>(static_cast<char*>(buffer.get()) +
207                                                             next);
208     }
209 
210     return true;
211 }
212 
GetAvailableDevices(std::vector<DmBlockDevice> * devices)213 bool DeviceMapper::GetAvailableDevices(std::vector<DmBlockDevice>* devices) {
214     devices->clear();
215 
216     // calculate the space needed to read a maximum of 256 targets, each with
217     // name with maximum length of 16 bytes
218     uint32_t payload_size = sizeof(struct dm_name_list);
219     // 128-bytes for the name
220     payload_size += DM_NAME_LEN;
221     // dm wants every device spec to be aligned at 8-byte boundary
222     payload_size = DM_ALIGN(payload_size);
223     payload_size *= kMaxPossibleDmDevices;
224     uint32_t data_size = sizeof(struct dm_ioctl) + payload_size;
225     auto buffer = std::unique_ptr<void, void (*)(void*)>(calloc(1, data_size), free);
226     if (buffer == nullptr) {
227         LOG(ERROR) << "failed to allocate memory";
228         return false;
229     }
230 
231     // Sets appropriate data size and data_start to make sure we tell kernel
232     // about the total size of the buffer we are passing and where to start
233     // writing the list of targets.
234     struct dm_ioctl* io = reinterpret_cast<struct dm_ioctl*>(buffer.get());
235     InitIo(io);
236     io->data_size = data_size;
237     io->data_start = sizeof(*io);
238 
239     if (ioctl(fd_, DM_LIST_DEVICES, io)) {
240         PLOG(ERROR) << "DM_LIST_DEVICES failed";
241         return false;
242     }
243 
244     // If the provided buffer wasn't enough to list all devices any data
245     // beyond sizeof(*io) must not be read.
246     if (io->flags & DM_BUFFER_FULL_FLAG) {
247         LOG(INFO) << data_size << " is not enough memory to list all dm devices";
248         return false;
249     }
250 
251     // if there are no devices created yet, return success with empty vector
252     if (io->data_size == sizeof(*io)) {
253         return true;
254     }
255 
256     // Parse each device and add a new DmBlockDevice to the vector
257     // created from the kernel data.
258     uint32_t next = sizeof(*io);
259     data_size = io->data_size - next;
260     struct dm_name_list* dm_dev =
261             reinterpret_cast<struct dm_name_list*>(static_cast<char*>(buffer.get()) + next);
262 
263     while (next && data_size) {
264         devices->emplace_back((dm_dev));
265         if (dm_dev->next == 0) {
266             break;
267         }
268         next += dm_dev->next;
269         data_size -= dm_dev->next;
270         dm_dev = reinterpret_cast<struct dm_name_list*>(static_cast<char*>(buffer.get()) + next);
271     }
272 
273     return true;
274 }
275 
276 // Accepts a device mapper device name (like system_a, vendor_b etc) and
277 // returns the path to it's device node (or symlink to the device node)
GetDmDevicePathByName(const std::string & name,std::string * path)278 bool DeviceMapper::GetDmDevicePathByName(const std::string& name, std::string* path) {
279     struct dm_ioctl io;
280     InitIo(&io, name);
281     if (ioctl(fd_, DM_DEV_STATUS, &io) < 0) {
282         PLOG(WARNING) << "DM_DEV_STATUS failed for " << name;
283         return false;
284     }
285 
286     uint32_t dev_num = minor(io.dev);
287     *path = "/dev/block/dm-" + std::to_string(dev_num);
288     return true;
289 }
290 
GetTableStatus(const std::string & name,std::vector<TargetInfo> * table)291 bool DeviceMapper::GetTableStatus(const std::string& name, std::vector<TargetInfo>* table) {
292     return GetTable(name, 0, table);
293 }
294 
GetTableInfo(const std::string & name,std::vector<TargetInfo> * table)295 bool DeviceMapper::GetTableInfo(const std::string& name, std::vector<TargetInfo>* table) {
296     return GetTable(name, DM_STATUS_TABLE_FLAG, table);
297 }
298 
299 // private methods of DeviceMapper
GetTable(const std::string & name,uint32_t flags,std::vector<TargetInfo> * table)300 bool DeviceMapper::GetTable(const std::string& name, uint32_t flags,
301                             std::vector<TargetInfo>* table) {
302     std::vector<char> buffer;
303     struct dm_ioctl* io = nullptr;
304 
305     for (buffer.resize(4096);; buffer.resize(buffer.size() * 2)) {
306         io = reinterpret_cast<struct dm_ioctl*>(&buffer[0]);
307 
308         InitIo(io, name);
309         io->data_size = buffer.size();
310         io->data_start = sizeof(*io);
311         io->flags = flags;
312         if (ioctl(fd_, DM_TABLE_STATUS, io) < 0) {
313             PLOG(ERROR) << "DM_TABLE_STATUS failed for " << name;
314             return false;
315         }
316         if (!(io->flags & DM_BUFFER_FULL_FLAG)) break;
317     }
318 
319     uint32_t cursor = io->data_start;
320     uint32_t data_end = std::min(io->data_size, uint32_t(buffer.size()));
321     for (uint32_t i = 0; i < io->target_count; i++) {
322         if (cursor + sizeof(struct dm_target_spec) > data_end) {
323             break;
324         }
325         // After each dm_target_spec is a status string. spec->next is an
326         // offset from |io->data_start|, and we clamp it to the size of our
327         // buffer.
328         struct dm_target_spec* spec = reinterpret_cast<struct dm_target_spec*>(&buffer[cursor]);
329         uint32_t data_offset = cursor + sizeof(dm_target_spec);
330         uint32_t next_cursor = std::min(io->data_start + spec->next, data_end);
331 
332         std::string data;
333         if (next_cursor > data_offset) {
334             // Note: we use c_str() to eliminate any extra trailing 0s.
335             data = std::string(&buffer[data_offset], next_cursor - data_offset).c_str();
336         }
337         table->emplace_back(*spec, data);
338         cursor = next_cursor;
339     }
340     return true;
341 }
342 
InitIo(struct dm_ioctl * io,const std::string & name) const343 void DeviceMapper::InitIo(struct dm_ioctl* io, const std::string& name) const {
344     CHECK(io != nullptr) << "nullptr passed to dm_ioctl initialization";
345     memset(io, 0, sizeof(*io));
346 
347     io->version[0] = DM_VERSION0;
348     io->version[1] = DM_VERSION1;
349     io->version[2] = DM_VERSION2;
350     io->data_size = sizeof(*io);
351     io->data_start = 0;
352     if (!name.empty()) {
353         snprintf(io->name, sizeof(io->name), "%s", name.c_str());
354     }
355 }
356 
357 }  // namespace dm
358 }  // namespace android
359