1 /*
2 * Support for decoding of DM_* ioctl commands.
3 *
4 * Copyright (c) 2016 Mikulas Patocka <mpatocka@redhat.com>
5 * Copyright (c) 2016 Masatake Yamato <yamato@redhat.com>
6 * Copyright (c) 2016 Dmitry V. Levin <ldv@altlinux.org>
7 * Copyright (c) 2016 Eugene Syromyatnikov <evgsyr@gmail.com>
8 * All rights reserved.
9 *
10 * Redistribution and use in source and binary forms, with or without
11 * modification, are permitted provided that the following conditions
12 * are met:
13 * 1. Redistributions of source code must retain the above copyright
14 * notice, this list of conditions and the following disclaimer.
15 * 2. Redistributions in binary form must reproduce the above copyright
16 * notice, this list of conditions and the following disclaimer in the
17 * documentation and/or other materials provided with the distribution.
18 * 3. The name of the author may not be used to endorse or promote products
19 * derived from this software without specific prior written permission.
20 *
21 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
22 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
23 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
24 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
25 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
26 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
27 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
28 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
29 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
30 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
31 */
32
33 #include "defs.h"
34
35 #ifdef HAVE_LINUX_DM_IOCTL_H
36
37 # include <linux/dm-ioctl.h>
38 # include <linux/ioctl.h>
39
40 # if DM_VERSION_MAJOR == 4
41
42 /* Definitions for command which have been added later */
43
44 # ifndef DM_LIST_VERSIONS
45 # define DM_LIST_VERSIONS _IOWR(DM_IOCTL, 0xd, struct dm_ioctl)
46 # endif
47 # ifndef DM_TARGET_MSG
48 # define DM_TARGET_MSG _IOWR(DM_IOCTL, 0xe, struct dm_ioctl)
49 # endif
50 # ifndef DM_DEV_SET_GEOMETRY
51 # define DM_DEV_SET_GEOMETRY _IOWR(DM_IOCTL, 0xf, struct dm_ioctl)
52 # endif
53
54
55 static void
dm_decode_device(const unsigned int code,const struct dm_ioctl * ioc)56 dm_decode_device(const unsigned int code, const struct dm_ioctl *ioc)
57 {
58 switch (code) {
59 case DM_REMOVE_ALL:
60 case DM_LIST_DEVICES:
61 case DM_LIST_VERSIONS:
62 break;
63 default:
64 if (ioc->dev) {
65 tprints(", dev=");
66 print_dev_t(ioc->dev);
67 }
68 if (ioc->name[0]) {
69 tprints(", name=");
70 print_quoted_string(ioc->name, DM_NAME_LEN,
71 QUOTE_0_TERMINATED);
72 }
73 if (ioc->uuid[0]) {
74 tprints(", uuid=");
75 print_quoted_string(ioc->uuid, DM_UUID_LEN,
76 QUOTE_0_TERMINATED);
77 }
78 break;
79 }
80 }
81
82 static void
dm_decode_values(struct tcb * tcp,const unsigned int code,const struct dm_ioctl * ioc)83 dm_decode_values(struct tcb *tcp, const unsigned int code,
84 const struct dm_ioctl *ioc)
85 {
86 if (entering(tcp)) {
87 switch (code) {
88 case DM_TABLE_LOAD:
89 tprintf(", target_count=%" PRIu32,
90 ioc->target_count);
91 break;
92 case DM_DEV_SUSPEND:
93 if (ioc->flags & DM_SUSPEND_FLAG)
94 break;
95 /* Fall through */
96 case DM_DEV_RENAME:
97 case DM_DEV_REMOVE:
98 case DM_DEV_WAIT:
99 tprintf(", event_nr=%" PRIu32,
100 ioc->event_nr);
101 break;
102 }
103 } else if (!syserror(tcp)) {
104 switch (code) {
105 case DM_DEV_CREATE:
106 case DM_DEV_RENAME:
107 case DM_DEV_SUSPEND:
108 case DM_DEV_STATUS:
109 case DM_DEV_WAIT:
110 case DM_TABLE_LOAD:
111 case DM_TABLE_CLEAR:
112 case DM_TABLE_DEPS:
113 case DM_TABLE_STATUS:
114 case DM_TARGET_MSG:
115 tprintf(", target_count=%" PRIu32,
116 ioc->target_count);
117 tprintf(", open_count=%" PRIu32,
118 ioc->open_count);
119 tprintf(", event_nr=%" PRIu32,
120 ioc->event_nr);
121 break;
122 }
123 }
124 }
125
126 #include "xlat/dm_flags.h"
127
128 static void
dm_decode_flags(const struct dm_ioctl * ioc)129 dm_decode_flags(const struct dm_ioctl *ioc)
130 {
131 tprints(", flags=");
132 printflags(dm_flags, ioc->flags, "DM_???");
133 }
134
135 static void
dm_decode_dm_target_spec(struct tcb * const tcp,const kernel_ulong_t addr,const struct dm_ioctl * const ioc)136 dm_decode_dm_target_spec(struct tcb *const tcp, const kernel_ulong_t addr,
137 const struct dm_ioctl *const ioc)
138 {
139 static const uint32_t target_spec_size =
140 sizeof(struct dm_target_spec);
141 uint32_t i;
142 uint32_t offset = ioc->data_start;
143 uint32_t offset_end;
144
145 if (abbrev(tcp)) {
146 if (ioc->target_count)
147 tprints(", ...");
148
149 return;
150 }
151
152 for (i = 0; i < ioc->target_count; i++) {
153 struct dm_target_spec s;
154
155 offset_end = offset + target_spec_size;
156
157 if (offset_end <= offset || offset_end > ioc->data_size)
158 goto misplaced;
159
160 tprints(", ");
161
162 if (i >= max_strlen) {
163 tprints("...");
164 break;
165 }
166
167 if (umove_or_printaddr(tcp, addr + offset, &s))
168 break;
169
170 tprintf("{sector_start=%" PRI__u64 ", length=%" PRI__u64,
171 s.sector_start, s.length);
172
173 if (exiting(tcp))
174 tprintf(", status=%" PRId32, s.status);
175
176 tprints(", target_type=");
177 print_quoted_string(s.target_type, DM_MAX_TYPE_NAME,
178 QUOTE_0_TERMINATED);
179
180 tprints(", string=");
181 printstr_ex(tcp, addr + offset_end, ioc->data_size - offset_end,
182 QUOTE_0_TERMINATED);
183 tprintf("}");
184
185 if (entering(tcp))
186 offset += s.next;
187 else
188 offset = ioc->data_start + s.next;
189
190 if (offset <= offset_end)
191 goto misplaced;
192 }
193
194 return;
195
196 misplaced:
197 tprints(", /* misplaced struct dm_target_spec */ ...");
198 }
199
200 bool
dm_print_dev(struct tcb * tcp,void * dev_ptr,size_t dev_size,void * dummy)201 dm_print_dev(struct tcb *tcp, void *dev_ptr, size_t dev_size, void *dummy)
202 {
203 uint64_t *dev = (uint64_t *) dev_ptr;
204
205 print_dev_t(*dev);
206
207 return 1;
208 }
209
210 static void
dm_decode_dm_target_deps(struct tcb * const tcp,const kernel_ulong_t addr,const struct dm_ioctl * const ioc)211 dm_decode_dm_target_deps(struct tcb *const tcp, const kernel_ulong_t addr,
212 const struct dm_ioctl *const ioc)
213 {
214 static const uint32_t target_deps_dev_offs =
215 offsetof(struct dm_target_deps, dev);
216 uint64_t dev_buf;
217 struct dm_target_deps s;
218 uint32_t offset = ioc->data_start;
219 uint32_t offset_end = offset + target_deps_dev_offs;
220 uint32_t space;
221
222 if (abbrev(tcp)) {
223 tprints(", ...");
224 return;
225 }
226
227 tprints(", ");
228
229 if (offset_end <= offset || offset_end > ioc->data_size)
230 goto misplaced;
231
232 if (umove_or_printaddr(tcp, addr + offset, &s))
233 return;
234
235 space = (ioc->data_size - offset_end) / sizeof(dev_buf);
236
237 if (s.count > space)
238 goto misplaced;
239
240 tprintf("{count=%u, deps=", s.count);
241
242 print_array(tcp, addr + offset_end, s.count, &dev_buf, sizeof(dev_buf),
243 umoven_or_printaddr, dm_print_dev, NULL);
244
245 tprints("}");
246
247 return;
248
249 misplaced:
250 tprints("/* misplaced struct dm_target_deps */ ...");
251 }
252
253 static void
dm_decode_dm_name_list(struct tcb * const tcp,const kernel_ulong_t addr,const struct dm_ioctl * const ioc)254 dm_decode_dm_name_list(struct tcb *const tcp, const kernel_ulong_t addr,
255 const struct dm_ioctl *const ioc)
256 {
257 static const uint32_t name_list_name_offs =
258 offsetof(struct dm_name_list, name);
259 struct dm_name_list s;
260 uint32_t offset = ioc->data_start;
261 uint32_t offset_end;
262 uint32_t count;
263
264 if (abbrev(tcp)) {
265 tprints(", ...");
266 return;
267 }
268
269 for (count = 0;; count++) {
270 offset_end = offset + name_list_name_offs;
271
272 if (offset_end <= offset || offset_end > ioc->data_size)
273 goto misplaced;
274
275 tprints(", ");
276
277 if (count >= max_strlen) {
278 tprints("...");
279 break;
280 }
281
282 if (umove_or_printaddr(tcp, addr + offset, &s))
283 break;
284 if (!count && !s.dev) {
285 tprints("/* no devices present */");
286 break;
287 }
288
289 tprints("{dev=");
290 print_dev_t(s.dev);
291
292 tprints("name=");
293 printstr_ex(tcp, addr + offset_end, ioc->data_size - offset_end,
294 QUOTE_0_TERMINATED);
295 tprints("}");
296
297 if (!s.next)
298 break;
299
300 offset += s.next;
301 if (offset <= offset_end)
302 goto misplaced;
303 }
304
305 return;
306
307 misplaced:
308 tprints(", /* misplaced struct dm_name_list */ ...");
309 }
310
311 static void
dm_decode_dm_target_versions(struct tcb * const tcp,const kernel_ulong_t addr,const struct dm_ioctl * const ioc)312 dm_decode_dm_target_versions(struct tcb *const tcp, const kernel_ulong_t addr,
313 const struct dm_ioctl *const ioc)
314 {
315 static const uint32_t target_vers_name_offs =
316 offsetof(struct dm_target_versions, name);
317 struct dm_target_versions s;
318 uint32_t offset = ioc->data_start;
319 uint32_t offset_end;
320 uint32_t count;
321
322 if (abbrev(tcp)) {
323 tprints(", ...");
324 return;
325 }
326
327 for (count = 0;; count++) {
328 offset_end = offset + target_vers_name_offs;
329
330 if (offset_end <= offset || offset_end > ioc->data_size)
331 goto misplaced;
332
333 tprints(", ");
334
335 if (count >= max_strlen) {
336 tprints("...");
337 break;
338 }
339
340 if (umove_or_printaddr(tcp, addr + offset, &s))
341 break;
342
343 tprints("{name=");
344 printstr_ex(tcp, addr + offset_end, ioc->data_size - offset_end,
345 QUOTE_0_TERMINATED);
346 tprintf(", version=%" PRIu32 ".%" PRIu32 ".%" PRIu32 "}",
347 s.version[0], s.version[1], s.version[2]);
348
349 if (!s.next)
350 break;
351
352 offset += s.next;
353 if (offset <= offset_end)
354 goto misplaced;
355 }
356
357 return;
358
359 misplaced:
360 tprints(", /* misplaced struct dm_target_versions */ ...");
361 }
362
363 static void
dm_decode_dm_target_msg(struct tcb * const tcp,const kernel_ulong_t addr,const struct dm_ioctl * const ioc)364 dm_decode_dm_target_msg(struct tcb *const tcp, const kernel_ulong_t addr,
365 const struct dm_ioctl *const ioc)
366 {
367 static const uint32_t target_msg_message_offs =
368 offsetof(struct dm_target_msg, message);
369 uint32_t offset = ioc->data_start;
370 uint32_t offset_end = offset + target_msg_message_offs;
371
372 if (abbrev(tcp)) {
373 tprints(", ...");
374 return;
375 }
376
377 if (offset_end > offset && offset_end <= ioc->data_size) {
378 struct dm_target_msg s;
379
380 tprints(", ");
381
382 if (umove_or_printaddr(tcp, addr + offset, &s))
383 return;
384
385 tprintf("{sector=%" PRI__u64 ", message=", s.sector);
386 printstr_ex(tcp, addr + offset_end, ioc->data_size - offset_end,
387 QUOTE_0_TERMINATED);
388 tprints("}");
389 } else {
390 tprints(", /* misplaced struct dm_target_msg */");
391 }
392 }
393
394 static void
dm_decode_string(struct tcb * const tcp,const kernel_ulong_t addr,const struct dm_ioctl * const ioc)395 dm_decode_string(struct tcb *const tcp, const kernel_ulong_t addr,
396 const struct dm_ioctl *const ioc)
397 {
398 uint32_t offset = ioc->data_start;
399
400 if (abbrev(tcp)) {
401 tprints(", ...");
402 return;
403 }
404
405 if (offset < ioc->data_size) {
406 tprints(", string=");
407 printstr_ex(tcp, addr + offset, ioc->data_size - offset,
408 QUOTE_0_TERMINATED);
409 } else {
410 tprints(", /* misplaced string */");
411 }
412 }
413
414 static inline bool
dm_ioctl_has_params(const unsigned int code)415 dm_ioctl_has_params(const unsigned int code)
416 {
417 switch (code) {
418 case DM_VERSION:
419 case DM_REMOVE_ALL:
420 case DM_DEV_CREATE:
421 case DM_DEV_REMOVE:
422 case DM_DEV_SUSPEND:
423 case DM_DEV_STATUS:
424 case DM_TABLE_CLEAR:
425 return false;
426 }
427
428 return true;
429 }
430
431 static int
dm_known_ioctl(struct tcb * const tcp,const unsigned int code,const kernel_ulong_t arg)432 dm_known_ioctl(struct tcb *const tcp, const unsigned int code,
433 const kernel_ulong_t arg)
434 {
435 struct dm_ioctl *ioc = NULL;
436 struct dm_ioctl *entering_ioc = NULL;
437 bool ioc_changed = false;
438
439 if (entering(tcp)) {
440 ioc = malloc(sizeof(*ioc));
441 if (!ioc)
442 return 0;
443 } else {
444 ioc = alloca(sizeof(*ioc));
445 }
446
447 if ((umoven(tcp, arg, offsetof(struct dm_ioctl, data), ioc) < 0) ||
448 (ioc->data_size < offsetof(struct dm_ioctl, data_size))) {
449 if (entering(tcp))
450 free(ioc);
451 return 0;
452 }
453 if (entering(tcp))
454 set_tcb_priv_data(tcp, ioc, free);
455 else {
456 entering_ioc = get_tcb_priv_data(tcp);
457
458 /*
459 * retrieve_status, __dev_status called only in case of success,
460 * so it looks like there's no need to check open_count,
461 * event_nr, target_count, dev fields for change (they are
462 * printed only in case of absence of errors).
463 */
464 if (!entering_ioc ||
465 (ioc->version[0] != entering_ioc->version[0]) ||
466 (ioc->version[1] != entering_ioc->version[1]) ||
467 (ioc->version[2] != entering_ioc->version[2]) ||
468 (ioc->data_size != entering_ioc->data_size) ||
469 (ioc->data_start != entering_ioc->data_start) ||
470 (ioc->flags != entering_ioc->flags))
471 ioc_changed = true;
472 }
473
474 if (exiting(tcp) && syserror(tcp) && !ioc_changed)
475 return 1;
476
477 /*
478 * device mapper code uses %d in some places and %u in another, but
479 * fields themselves are declared as __u32.
480 */
481 tprintf("%s{version=%u.%u.%u", entering(tcp) ? ", " : " => ",
482 ioc->version[0], ioc->version[1], ioc->version[2]);
483 /*
484 * if we use a different version of ABI, do not attempt to decode
485 * ioctl fields
486 */
487 if (ioc->version[0] != DM_VERSION_MAJOR) {
488 tprints(", /* Unsupported device mapper ABI version */ ...");
489 goto skip;
490 }
491
492 tprintf(", data_size=%u", ioc->data_size);
493
494 if (dm_ioctl_has_params(code))
495 tprintf(", data_start=%u", ioc->data_start);
496
497 if (ioc->data_size < offsetof(struct dm_ioctl, data)) {
498 tprints(", /* Incorrect data_size */ ...");
499 goto skip;
500 }
501
502 dm_decode_device(code, ioc);
503 dm_decode_values(tcp, code, ioc);
504 dm_decode_flags(ioc);
505
506 switch (code) {
507 case DM_DEV_WAIT:
508 case DM_TABLE_STATUS:
509 if (entering(tcp) || syserror(tcp))
510 break;
511 dm_decode_dm_target_spec(tcp, arg, ioc);
512 break;
513 case DM_TABLE_LOAD:
514 if (exiting(tcp))
515 break;
516 dm_decode_dm_target_spec(tcp, arg, ioc);
517 break;
518 case DM_TABLE_DEPS:
519 if (entering(tcp) || syserror(tcp))
520 break;
521 dm_decode_dm_target_deps(tcp, arg, ioc);
522 break;
523 case DM_LIST_DEVICES:
524 if (entering(tcp) || syserror(tcp))
525 break;
526 dm_decode_dm_name_list(tcp, arg, ioc);
527 break;
528 case DM_LIST_VERSIONS:
529 if (entering(tcp) || syserror(tcp))
530 break;
531 dm_decode_dm_target_versions(tcp, arg, ioc);
532 break;
533 case DM_TARGET_MSG:
534 if (entering(tcp))
535 dm_decode_dm_target_msg(tcp, arg, ioc);
536 else if (!syserror(tcp) && ioc->flags & DM_DATA_OUT_FLAG)
537 dm_decode_string(tcp, arg, ioc);
538 break;
539 case DM_DEV_RENAME:
540 case DM_DEV_SET_GEOMETRY:
541 if (exiting(tcp))
542 break;
543 dm_decode_string(tcp, arg, ioc);
544 break;
545 }
546
547 skip:
548 tprints("}");
549 return 1;
550 }
551
552 int
dm_ioctl(struct tcb * const tcp,const unsigned int code,const kernel_ulong_t arg)553 dm_ioctl(struct tcb *const tcp, const unsigned int code, const kernel_ulong_t arg)
554 {
555 switch (code) {
556 case DM_VERSION:
557 case DM_REMOVE_ALL:
558 case DM_LIST_DEVICES:
559 case DM_DEV_CREATE:
560 case DM_DEV_REMOVE:
561 case DM_DEV_RENAME:
562 case DM_DEV_SUSPEND:
563 case DM_DEV_STATUS:
564 case DM_DEV_WAIT:
565 case DM_TABLE_LOAD:
566 case DM_TABLE_CLEAR:
567 case DM_TABLE_DEPS:
568 case DM_TABLE_STATUS:
569 case DM_LIST_VERSIONS:
570 case DM_TARGET_MSG:
571 case DM_DEV_SET_GEOMETRY:
572 return dm_known_ioctl(tcp, code, arg);
573 default:
574 return 0;
575 }
576 }
577
578 # else /* !(DM_VERSION_MAJOR == 4) */
579
580 int
dm_ioctl(struct tcb * const tcp,const unsigned int code,const kernel_ulong_t arg)581 dm_ioctl(struct tcb *const tcp, const unsigned int code, const kernel_ulong_t arg)
582 {
583 return 0;
584 }
585
586 # endif /* DM_VERSION_MAJOR == 4 */
587 #endif /* HAVE_LINUX_DM_IOCTL_H */
588