2 * Support for decoding of DM_* ioctl commands.
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 * Copyright (c) 2016-2018 The strace developers.
11 * SPDX-License-Identifier: LGPL-2.1-or-later
16 #ifdef HAVE_LINUX_DM_IOCTL_H
18 # include "print_fields.h"
19 # include <linux/dm-ioctl.h>
20 # include <linux/ioctl.h>
22 # if DM_VERSION_MAJOR == 4
24 /* Definitions for command which have been added later */
26 # ifndef DM_LIST_VERSIONS
27 # define DM_LIST_VERSIONS _IOWR(DM_IOCTL, 0x0d, struct dm_ioctl)
29 # ifndef DM_TARGET_MSG
30 # define DM_TARGET_MSG _IOWR(DM_IOCTL, 0x0e, struct dm_ioctl)
32 # ifndef DM_DEV_SET_GEOMETRY
33 # define DM_DEV_SET_GEOMETRY _IOWR(DM_IOCTL, 0x0f, struct dm_ioctl)
35 # ifndef DM_DEV_ARM_POLL
36 # define DM_DEV_ARM_POLL _IOWR(DM_IOCTL, 0x10, struct dm_ioctl)
41 dm_decode_device(const unsigned int code, const struct dm_ioctl *ioc)
46 case DM_LIST_VERSIONS:
50 PRINT_FIELD_DEV(", ", *ioc, dev);
53 PRINT_FIELD_CSTRING(", ", *ioc, name);
56 PRINT_FIELD_CSTRING(", ", *ioc, uuid);
63 dm_decode_values(struct tcb *tcp, const unsigned int code,
64 const struct dm_ioctl *ioc)
69 PRINT_FIELD_U(", ", *ioc, target_count);
72 if (ioc->flags & DM_SUSPEND_FLAG)
74 ATTRIBUTE_FALLTHROUGH;
78 PRINT_FIELD_U(", ", *ioc, event_nr);
81 } else if (!syserror(tcp)) {
93 PRINT_FIELD_U(", ", *ioc, target_count);
94 PRINT_FIELD_U(", ", *ioc, open_count);
95 PRINT_FIELD_U(", ", *ioc, event_nr);
101 # include "xlat/dm_flags.h"
104 dm_decode_flags(const struct dm_ioctl *ioc)
106 PRINT_FIELD_FLAGS(", ", *ioc, flags, dm_flags, "DM_???");
110 dm_decode_dm_target_spec(struct tcb *const tcp, const kernel_ulong_t addr,
111 const struct dm_ioctl *const ioc)
113 static const uint32_t target_spec_size =
114 sizeof(struct dm_target_spec);
116 uint32_t offset = ioc->data_start;
117 uint32_t offset_end = 0;
120 if (ioc->target_count)
126 for (i = 0; i < ioc->target_count; i++) {
129 if (i && offset <= offset_end)
132 offset_end = offset + target_spec_size;
134 if (offset_end <= offset || offset_end > ioc->data_size)
137 if (i >= max_strlen) {
142 struct dm_target_spec s;
144 if (umove_or_printaddr(tcp, addr + offset, &s))
147 PRINT_FIELD_U("{", s, sector_start);
148 PRINT_FIELD_U(", ", s, length);
151 PRINT_FIELD_D(", ", s, status);
153 PRINT_FIELD_CSTRING(", ", s, target_type);
155 tprints(", string=");
156 printstr_ex(tcp, addr + offset_end, ioc->data_size - offset_end,
163 offset = ioc->data_start + s.next;
170 tprints_comment("misplaced struct dm_target_spec");
174 dm_print_dev(struct tcb *tcp, void *dev_ptr, size_t dev_size, void *dummy)
176 uint64_t *dev = (uint64_t *) dev_ptr;
184 dm_decode_dm_target_deps(struct tcb *const tcp, const kernel_ulong_t addr,
185 const struct dm_ioctl *const ioc)
187 if (ioc->data_start == ioc->data_size)
197 static const uint32_t target_deps_dev_offs =
198 offsetof(struct dm_target_deps, dev);
200 struct dm_target_deps s;
201 uint32_t offset = ioc->data_start;
202 uint32_t offset_end = offset + target_deps_dev_offs;
205 if (offset_end <= offset || offset_end > ioc->data_size)
208 if (umove_or_printaddr(tcp, addr + offset, &s))
211 space = (ioc->data_size - offset_end) / sizeof(dev_buf);
216 PRINT_FIELD_U("{", s, count);
219 print_array(tcp, addr + offset_end, s.count, &dev_buf, sizeof(dev_buf),
220 tfetch_mem, dm_print_dev, NULL);
228 tprints_comment("misplaced struct dm_target_deps");
232 dm_decode_dm_name_list(struct tcb *const tcp, const kernel_ulong_t addr,
233 const struct dm_ioctl *const ioc)
235 static const uint32_t name_list_name_offs =
236 offsetof(struct dm_name_list, name);
237 struct dm_name_list s;
238 uint32_t offset = ioc->data_start;
239 uint32_t offset_end = 0;
243 if (ioc->data_start == ioc->data_size)
251 for (count = 0;; count++) {
254 if (count && offset <= offset_end)
257 offset_end = offset + name_list_name_offs;
259 if (offset_end <= offset || offset_end > ioc->data_size)
262 if (count >= max_strlen) {
267 if (umove_or_printaddr(tcp, addr + offset, &s))
270 PRINT_FIELD_DEV("{", s, dev);
272 rc = printstr_ex(tcp, addr + offset_end,
273 ioc->data_size - offset_end,
277 * In Linux v4.13-rc1~137^2~13 it has been decided to cram in
278 * one more undocumented field after the device name, as if the
279 * format decoding was not twisted enough already. So, we have
280 * to check "next" now, and if it _looks like_ that there is
281 * a space for one additional integer, let's print it. As if the
282 * perversity with "name string going further than pointer to
283 * the next one" wasn't enough. Moreover, the calculation was
284 * broken for m32 on 64-bit kernels until v4.14-rc4~20^2~3, and
285 * we have no ability to detect kernel bit-ness (on x86, at
286 * least), so refrain from printing it for the DM versions below
287 * 4.37 (the original version was also aligned differently than
288 * now even on 64 bit).
291 if ((rc > 0) && ioc->version[1] >= 37) {
292 kernel_ulong_t event_addr =
293 (addr + offset_end + rc + 7) & ~7;
296 if ((event_addr + sizeof(event_nr)) <=
297 (addr + offset + s.next) &&
298 !umove(tcp, event_addr, &event_nr))
299 tprintf(", event_nr=%" PRIu32, event_nr);
314 tprints_comment("misplaced struct dm_name_list");
318 dm_decode_dm_target_versions(struct tcb *const tcp, const kernel_ulong_t addr,
319 const struct dm_ioctl *const ioc)
321 static const uint32_t target_vers_name_offs =
322 offsetof(struct dm_target_versions, name);
323 struct dm_target_versions s;
324 uint32_t offset = ioc->data_start;
325 uint32_t offset_end = 0;
328 if (ioc->data_start == ioc->data_size)
336 for (count = 0;; count++) {
339 if (count && offset <= offset_end)
342 offset_end = offset + target_vers_name_offs;
344 if (offset_end <= offset || offset_end > ioc->data_size)
347 if (count >= max_strlen) {
352 if (umove_or_printaddr(tcp, addr + offset, &s))
356 printstr_ex(tcp, addr + offset_end, ioc->data_size - offset_end,
358 tprintf(", version=%" PRIu32 ".%" PRIu32 ".%" PRIu32 "}",
359 s.version[0], s.version[1], s.version[2]);
371 tprints_comment("misplaced struct dm_target_versions");
375 dm_decode_dm_target_msg(struct tcb *const tcp, const kernel_ulong_t addr,
376 const struct dm_ioctl *const ioc)
378 if (ioc->data_start == ioc->data_size)
388 static const uint32_t target_msg_message_offs =
389 offsetof(struct dm_target_msg, message);
390 uint32_t offset = ioc->data_start;
391 uint32_t offset_end = offset + target_msg_message_offs;
393 if (offset_end > offset && offset_end <= ioc->data_size) {
394 struct dm_target_msg s;
396 if (umove_or_printaddr(tcp, addr + offset, &s))
399 PRINT_FIELD_U("{", s, sector);
400 tprints(", message=");
401 printstr_ex(tcp, addr + offset_end, ioc->data_size - offset_end,
406 tprints_comment("misplaced struct dm_target_msg");
411 dm_decode_string(struct tcb *const tcp, const kernel_ulong_t addr,
412 const struct dm_ioctl *const ioc)
421 uint32_t offset = ioc->data_start;
423 if (offset <= ioc->data_size) {
425 printstr_ex(tcp, addr + offset, ioc->data_size - offset,
429 tprints_comment("misplaced string");
434 dm_ioctl_has_params(const unsigned int code)
444 case DM_DEV_ARM_POLL:
452 dm_known_ioctl(struct tcb *const tcp, const unsigned int code,
453 const kernel_ulong_t arg)
455 struct dm_ioctl *ioc = NULL;
456 struct dm_ioctl *entering_ioc = NULL;
457 bool ioc_changed = false;
460 ioc = malloc(sizeof(*ioc));
464 ioc = alloca(sizeof(*ioc));
467 if ((umoven(tcp, arg, offsetof(struct dm_ioctl, data), ioc) < 0) ||
468 (ioc->data_size < offsetof(struct dm_ioctl, data_size))) {
474 set_tcb_priv_data(tcp, ioc, free);
476 entering_ioc = get_tcb_priv_data(tcp);
479 * retrieve_status, __dev_status called only in case of success,
480 * so it looks like there's no need to check open_count,
481 * event_nr, target_count, dev fields for change (they are
482 * printed only in case of absence of errors).
485 (ioc->version[0] != entering_ioc->version[0]) ||
486 (ioc->version[1] != entering_ioc->version[1]) ||
487 (ioc->version[2] != entering_ioc->version[2]) ||
488 (ioc->data_size != entering_ioc->data_size) ||
489 (ioc->data_start != entering_ioc->data_start) ||
490 (ioc->flags != entering_ioc->flags))
494 if (exiting(tcp) && syserror(tcp) && !ioc_changed)
495 return RVAL_IOCTL_DECODED;
498 * device mapper code uses %d in some places and %u in another, but
499 * fields themselves are declared as __u32.
501 tprintf("%s{version=%u.%u.%u", entering(tcp) ? ", " : " => ",
502 ioc->version[0], ioc->version[1], ioc->version[2]);
504 * if we use a different version of ABI, do not attempt to decode
507 if (ioc->version[0] != DM_VERSION_MAJOR) {
508 tprints_comment("unsupported device mapper ABI version");
512 PRINT_FIELD_U(", ", *ioc, data_size);
514 if (ioc->data_size < offsetof(struct dm_ioctl, data)) {
515 tprints_comment("data_size too small");
519 if (dm_ioctl_has_params(code))
520 PRINT_FIELD_U(", ", *ioc, data_start);
522 dm_decode_device(code, ioc);
523 dm_decode_values(tcp, code, ioc);
524 dm_decode_flags(ioc);
528 case DM_TABLE_STATUS:
529 if (entering(tcp) || syserror(tcp))
531 dm_decode_dm_target_spec(tcp, arg, ioc);
536 dm_decode_dm_target_spec(tcp, arg, ioc);
539 if (entering(tcp) || syserror(tcp))
541 dm_decode_dm_target_deps(tcp, arg, ioc);
543 case DM_LIST_DEVICES:
544 if (entering(tcp) || syserror(tcp))
546 dm_decode_dm_name_list(tcp, arg, ioc);
548 case DM_LIST_VERSIONS:
549 if (entering(tcp) || syserror(tcp))
551 dm_decode_dm_target_versions(tcp, arg, ioc);
555 dm_decode_dm_target_msg(tcp, arg, ioc);
556 else if (!syserror(tcp) && ioc->flags & DM_DATA_OUT_FLAG)
557 dm_decode_string(tcp, arg, ioc);
560 case DM_DEV_SET_GEOMETRY:
563 dm_decode_string(tcp, arg, ioc);
569 return entering(tcp) ? 0 : RVAL_IOCTL_DECODED;
573 dm_ioctl(struct tcb *const tcp, const unsigned int code, const kernel_ulong_t arg)
578 case DM_LIST_DEVICES:
588 case DM_TABLE_STATUS:
589 case DM_LIST_VERSIONS:
591 case DM_DEV_SET_GEOMETRY:
592 case DM_DEV_ARM_POLL:
593 return dm_known_ioctl(tcp, code, arg);
599 # else /* !(DM_VERSION_MAJOR == 4) */
602 dm_ioctl(struct tcb *const tcp, const unsigned int code, const kernel_ulong_t arg)
607 # endif /* DM_VERSION_MAJOR == 4 */
608 #endif /* HAVE_LINUX_DM_IOCTL_H */