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