|  | /* | 
|  | * Support for decoding of DM_* ioctl commands. | 
|  | * | 
|  | * Copyright (c) 2016 Mikulas Patocka <mpatocka@redhat.com> | 
|  | * Copyright (c) 2016 Masatake Yamato <yamato@redhat.com> | 
|  | * Copyright (c) 2016 Dmitry V. Levin <ldv@altlinux.org> | 
|  | * Copyright (c) 2016 Eugene Syromyatnikov <evgsyr@gmail.com> | 
|  | * Copyright (c) 2016-2018 The strace developers. | 
|  | * All rights reserved. | 
|  | * | 
|  | * Redistribution and use in source and binary forms, with or without | 
|  | * modification, are permitted provided that the following conditions | 
|  | * are met: | 
|  | * 1. Redistributions of source code must retain the above copyright | 
|  | *    notice, this list of conditions and the following disclaimer. | 
|  | * 2. Redistributions in binary form must reproduce the above copyright | 
|  | *    notice, this list of conditions and the following disclaimer in the | 
|  | *    documentation and/or other materials provided with the distribution. | 
|  | * 3. The name of the author may not be used to endorse or promote products | 
|  | *    derived from this software without specific prior written permission. | 
|  | * | 
|  | * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR | 
|  | * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES | 
|  | * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. | 
|  | * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, | 
|  | * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT | 
|  | * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, | 
|  | * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY | 
|  | * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | 
|  | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF | 
|  | * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | 
|  | */ | 
|  |  | 
|  | #include "defs.h" | 
|  |  | 
|  | #ifdef HAVE_LINUX_DM_IOCTL_H | 
|  |  | 
|  | # include "print_fields.h" | 
|  | # include <linux/dm-ioctl.h> | 
|  | # include <linux/ioctl.h> | 
|  |  | 
|  | # if DM_VERSION_MAJOR == 4 | 
|  |  | 
|  | /* Definitions for command which have been added later */ | 
|  |  | 
|  | #  ifndef DM_LIST_VERSIONS | 
|  | #   define DM_LIST_VERSIONS    _IOWR(DM_IOCTL, 0x0d, struct dm_ioctl) | 
|  | #  endif | 
|  | #  ifndef DM_TARGET_MSG | 
|  | #   define DM_TARGET_MSG       _IOWR(DM_IOCTL, 0x0e, struct dm_ioctl) | 
|  | #  endif | 
|  | #  ifndef DM_DEV_SET_GEOMETRY | 
|  | #   define DM_DEV_SET_GEOMETRY _IOWR(DM_IOCTL, 0x0f, struct dm_ioctl) | 
|  | #  endif | 
|  | #  ifndef DM_DEV_ARM_POLL | 
|  | #   define DM_DEV_ARM_POLL     _IOWR(DM_IOCTL, 0x10, struct dm_ioctl) | 
|  | #  endif | 
|  |  | 
|  |  | 
|  | static void | 
|  | dm_decode_device(const unsigned int code, const struct dm_ioctl *ioc) | 
|  | { | 
|  | switch (code) { | 
|  | case DM_REMOVE_ALL: | 
|  | case DM_LIST_DEVICES: | 
|  | case DM_LIST_VERSIONS: | 
|  | break; | 
|  | default: | 
|  | if (ioc->dev) | 
|  | PRINT_FIELD_DEV(", ", *ioc, dev); | 
|  |  | 
|  | if (ioc->name[0]) | 
|  | PRINT_FIELD_CSTRING(", ", *ioc, name); | 
|  |  | 
|  | if (ioc->uuid[0]) | 
|  | PRINT_FIELD_CSTRING(", ", *ioc, uuid); | 
|  |  | 
|  | break; | 
|  | } | 
|  | } | 
|  |  | 
|  | static void | 
|  | dm_decode_values(struct tcb *tcp, const unsigned int code, | 
|  | const struct dm_ioctl *ioc) | 
|  | { | 
|  | if (entering(tcp)) { | 
|  | switch (code) { | 
|  | case DM_TABLE_LOAD: | 
|  | PRINT_FIELD_U(", ", *ioc, target_count); | 
|  | break; | 
|  | case DM_DEV_SUSPEND: | 
|  | if (ioc->flags & DM_SUSPEND_FLAG) | 
|  | break; | 
|  | ATTRIBUTE_FALLTHROUGH; | 
|  | case DM_DEV_RENAME: | 
|  | case DM_DEV_REMOVE: | 
|  | case DM_DEV_WAIT: | 
|  | PRINT_FIELD_U(", ", *ioc, event_nr); | 
|  | break; | 
|  | } | 
|  | } else if (!syserror(tcp)) { | 
|  | switch (code) { | 
|  | case DM_DEV_CREATE: | 
|  | case DM_DEV_RENAME: | 
|  | case DM_DEV_SUSPEND: | 
|  | case DM_DEV_STATUS: | 
|  | case DM_DEV_WAIT: | 
|  | case DM_TABLE_LOAD: | 
|  | case DM_TABLE_CLEAR: | 
|  | case DM_TABLE_DEPS: | 
|  | case DM_TABLE_STATUS: | 
|  | case DM_TARGET_MSG: | 
|  | PRINT_FIELD_U(", ", *ioc, target_count); | 
|  | PRINT_FIELD_U(", ", *ioc, open_count); | 
|  | PRINT_FIELD_U(", ", *ioc, event_nr); | 
|  | break; | 
|  | } | 
|  | } | 
|  | } | 
|  |  | 
|  | #include "xlat/dm_flags.h" | 
|  |  | 
|  | static void | 
|  | dm_decode_flags(const struct dm_ioctl *ioc) | 
|  | { | 
|  | PRINT_FIELD_FLAGS(", ", *ioc, flags, dm_flags, "DM_???"); | 
|  | } | 
|  |  | 
|  | static void | 
|  | dm_decode_dm_target_spec(struct tcb *const tcp, const kernel_ulong_t addr, | 
|  | const struct dm_ioctl *const ioc) | 
|  | { | 
|  | static const uint32_t target_spec_size = | 
|  | sizeof(struct dm_target_spec); | 
|  | uint32_t i; | 
|  | uint32_t offset = ioc->data_start; | 
|  | uint32_t offset_end = 0; | 
|  |  | 
|  | if (abbrev(tcp)) { | 
|  | if (ioc->target_count) | 
|  | tprints(", ..."); | 
|  |  | 
|  | return; | 
|  | } | 
|  |  | 
|  | for (i = 0; i < ioc->target_count; i++) { | 
|  | tprints(", "); | 
|  |  | 
|  | if (i && offset <= offset_end) | 
|  | goto misplaced; | 
|  |  | 
|  | offset_end = offset + target_spec_size; | 
|  |  | 
|  | if (offset_end <= offset || offset_end > ioc->data_size) | 
|  | goto misplaced; | 
|  |  | 
|  | if (i >= max_strlen) { | 
|  | tprints("..."); | 
|  | break; | 
|  | } | 
|  |  | 
|  | struct dm_target_spec s; | 
|  |  | 
|  | if (umove_or_printaddr(tcp, addr + offset, &s)) | 
|  | break; | 
|  |  | 
|  | PRINT_FIELD_U("{", s, sector_start); | 
|  | PRINT_FIELD_U(", ", s, length); | 
|  |  | 
|  | if (exiting(tcp)) | 
|  | PRINT_FIELD_D(", ", s, status); | 
|  |  | 
|  | PRINT_FIELD_CSTRING(", ", s, target_type); | 
|  |  | 
|  | tprints(", string="); | 
|  | printstr_ex(tcp, addr + offset_end, ioc->data_size - offset_end, | 
|  | QUOTE_0_TERMINATED); | 
|  | tprints("}"); | 
|  |  | 
|  | if (entering(tcp)) | 
|  | offset += s.next; | 
|  | else | 
|  | offset = ioc->data_start + s.next; | 
|  | } | 
|  |  | 
|  | return; | 
|  |  | 
|  | misplaced: | 
|  | tprints("???"); | 
|  | tprints_comment("misplaced struct dm_target_spec"); | 
|  | } | 
|  |  | 
|  | bool | 
|  | dm_print_dev(struct tcb *tcp, void *dev_ptr, size_t dev_size, void *dummy) | 
|  | { | 
|  | uint64_t *dev = (uint64_t *) dev_ptr; | 
|  |  | 
|  | print_dev_t(*dev); | 
|  |  | 
|  | return 1; | 
|  | } | 
|  |  | 
|  | static void | 
|  | dm_decode_dm_target_deps(struct tcb *const tcp, const kernel_ulong_t addr, | 
|  | const struct dm_ioctl *const ioc) | 
|  | { | 
|  | if (ioc->data_start == ioc->data_size) | 
|  | return; | 
|  |  | 
|  | tprints(", "); | 
|  |  | 
|  | if (abbrev(tcp)) { | 
|  | tprints("..."); | 
|  | return; | 
|  | } | 
|  |  | 
|  | static const uint32_t target_deps_dev_offs = | 
|  | offsetof(struct dm_target_deps, dev); | 
|  | uint64_t dev_buf; | 
|  | struct dm_target_deps s; | 
|  | uint32_t offset = ioc->data_start; | 
|  | uint32_t offset_end = offset + target_deps_dev_offs; | 
|  | uint32_t space; | 
|  |  | 
|  | if (offset_end <= offset || offset_end > ioc->data_size) | 
|  | goto misplaced; | 
|  |  | 
|  | if (umove_or_printaddr(tcp, addr + offset, &s)) | 
|  | return; | 
|  |  | 
|  | space = (ioc->data_size - offset_end) / sizeof(dev_buf); | 
|  |  | 
|  | if (s.count > space) | 
|  | goto misplaced; | 
|  |  | 
|  | PRINT_FIELD_U("{", s, count); | 
|  |  | 
|  | tprints(", deps="); | 
|  | print_array(tcp, addr + offset_end, s.count, &dev_buf, sizeof(dev_buf), | 
|  | tfetch_mem, dm_print_dev, NULL); | 
|  |  | 
|  | tprints("}"); | 
|  |  | 
|  | return; | 
|  |  | 
|  | misplaced: | 
|  | tprints("???"); | 
|  | tprints_comment("misplaced struct dm_target_deps"); | 
|  | } | 
|  |  | 
|  | static void | 
|  | dm_decode_dm_name_list(struct tcb *const tcp, const kernel_ulong_t addr, | 
|  | const struct dm_ioctl *const ioc) | 
|  | { | 
|  | static const uint32_t name_list_name_offs = | 
|  | offsetof(struct dm_name_list, name); | 
|  | struct dm_name_list s; | 
|  | uint32_t offset = ioc->data_start; | 
|  | uint32_t offset_end = 0; | 
|  | uint32_t count; | 
|  | int rc; | 
|  |  | 
|  | if (ioc->data_start == ioc->data_size) | 
|  | return; | 
|  |  | 
|  | if (abbrev(tcp)) { | 
|  | tprints(", ..."); | 
|  | return; | 
|  | } | 
|  |  | 
|  | for (count = 0;; count++) { | 
|  | tprints(", "); | 
|  |  | 
|  | if (count && offset <= offset_end) | 
|  | goto misplaced; | 
|  |  | 
|  | offset_end = offset + name_list_name_offs; | 
|  |  | 
|  | if (offset_end <= offset || offset_end > ioc->data_size) | 
|  | goto misplaced; | 
|  |  | 
|  | if (count >= max_strlen) { | 
|  | tprints("..."); | 
|  | break; | 
|  | } | 
|  |  | 
|  | if (umove_or_printaddr(tcp, addr + offset, &s)) | 
|  | break; | 
|  |  | 
|  | PRINT_FIELD_DEV("{", s, dev); | 
|  | tprints(", name="); | 
|  | rc = printstr_ex(tcp, addr + offset_end, | 
|  | ioc->data_size - offset_end, | 
|  | QUOTE_0_TERMINATED); | 
|  |  | 
|  | /* | 
|  | * In Linux v4.13-rc1~137^2~13 it has been decided to cram in | 
|  | * one more undocumented field after the device name, as if the | 
|  | * format decoding was not twisted enough already. So, we have | 
|  | * to check "next" now, and if it _looks like_ that there is | 
|  | * a space for one additional integer, let's print it. As if the | 
|  | * perversity with "name string going further than pointer to | 
|  | * the next one" wasn't enough. Moreover, the calculation was | 
|  | * broken for m32 on 64-bit kernels until v4.14-rc4~20^2~3, and | 
|  | * we have no ability to detect kernel bit-ness (on x86, at | 
|  | * least), so refrain from printing it for the DM versions below | 
|  | * 4.37 (the original version was also aligned differently than | 
|  | * now even on 64 bit). | 
|  | */ | 
|  |  | 
|  | if ((rc > 0) && ioc->version[1] >= 37) { | 
|  | kernel_ulong_t event_addr = | 
|  | (addr + offset_end + rc + 7) & ~7; | 
|  | uint32_t event_nr; | 
|  |  | 
|  | if ((event_addr + sizeof(event_nr)) <= | 
|  | (addr + offset + s.next) && | 
|  | !umove(tcp, event_addr, &event_nr)) | 
|  | tprintf(", event_nr=%" PRIu32, event_nr); | 
|  | } | 
|  |  | 
|  | tprints("}"); | 
|  |  | 
|  | if (!s.next) | 
|  | break; | 
|  |  | 
|  | offset += s.next; | 
|  | } | 
|  |  | 
|  | return; | 
|  |  | 
|  | misplaced: | 
|  | tprints("???"); | 
|  | tprints_comment("misplaced struct dm_name_list"); | 
|  | } | 
|  |  | 
|  | static void | 
|  | dm_decode_dm_target_versions(struct tcb *const tcp, const kernel_ulong_t addr, | 
|  | const struct dm_ioctl *const ioc) | 
|  | { | 
|  | static const uint32_t target_vers_name_offs = | 
|  | offsetof(struct dm_target_versions, name); | 
|  | struct dm_target_versions s; | 
|  | uint32_t offset = ioc->data_start; | 
|  | uint32_t offset_end = 0; | 
|  | uint32_t count; | 
|  |  | 
|  | if (ioc->data_start == ioc->data_size) | 
|  | return; | 
|  |  | 
|  | if (abbrev(tcp)) { | 
|  | tprints(", ..."); | 
|  | return; | 
|  | } | 
|  |  | 
|  | for (count = 0;; count++) { | 
|  | tprints(", "); | 
|  |  | 
|  | if (count && offset <= offset_end) | 
|  | goto misplaced; | 
|  |  | 
|  | offset_end = offset + target_vers_name_offs; | 
|  |  | 
|  | if (offset_end <= offset || offset_end > ioc->data_size) | 
|  | goto misplaced; | 
|  |  | 
|  | if (count >= max_strlen) { | 
|  | tprints("..."); | 
|  | break; | 
|  | } | 
|  |  | 
|  | if (umove_or_printaddr(tcp, addr + offset, &s)) | 
|  | break; | 
|  |  | 
|  | tprints("{name="); | 
|  | printstr_ex(tcp, addr + offset_end, ioc->data_size - offset_end, | 
|  | QUOTE_0_TERMINATED); | 
|  | tprintf(", version=%" PRIu32 ".%" PRIu32 ".%" PRIu32 "}", | 
|  | s.version[0], s.version[1], s.version[2]); | 
|  |  | 
|  | if (!s.next) | 
|  | break; | 
|  |  | 
|  | offset += s.next; | 
|  | } | 
|  |  | 
|  | return; | 
|  |  | 
|  | misplaced: | 
|  | tprints("???"); | 
|  | tprints_comment("misplaced struct dm_target_versions"); | 
|  | } | 
|  |  | 
|  | static void | 
|  | dm_decode_dm_target_msg(struct tcb *const tcp, const kernel_ulong_t addr, | 
|  | const struct dm_ioctl *const ioc) | 
|  | { | 
|  | if (ioc->data_start == ioc->data_size) | 
|  | return; | 
|  |  | 
|  | tprints(", "); | 
|  |  | 
|  | if (abbrev(tcp)) { | 
|  | tprints("..."); | 
|  | return; | 
|  | } | 
|  |  | 
|  | static const uint32_t target_msg_message_offs = | 
|  | offsetof(struct dm_target_msg, message); | 
|  | uint32_t offset = ioc->data_start; | 
|  | uint32_t offset_end = offset + target_msg_message_offs; | 
|  |  | 
|  | if (offset_end > offset && offset_end <= ioc->data_size) { | 
|  | struct dm_target_msg s; | 
|  |  | 
|  | if (umove_or_printaddr(tcp, addr + offset, &s)) | 
|  | return; | 
|  |  | 
|  | PRINT_FIELD_U("{", s, sector); | 
|  | tprints(", message="); | 
|  | printstr_ex(tcp, addr + offset_end, ioc->data_size - offset_end, | 
|  | QUOTE_0_TERMINATED); | 
|  | tprints("}"); | 
|  | } else { | 
|  | tprints("???"); | 
|  | tprints_comment("misplaced struct dm_target_msg"); | 
|  | } | 
|  | } | 
|  |  | 
|  | static void | 
|  | dm_decode_string(struct tcb *const tcp, const kernel_ulong_t addr, | 
|  | const struct dm_ioctl *const ioc) | 
|  | { | 
|  | tprints(", "); | 
|  |  | 
|  | if (abbrev(tcp)) { | 
|  | tprints("..."); | 
|  | return; | 
|  | } | 
|  |  | 
|  | uint32_t offset = ioc->data_start; | 
|  |  | 
|  | if (offset <= ioc->data_size) { | 
|  | tprints("string="); | 
|  | printstr_ex(tcp, addr + offset, ioc->data_size - offset, | 
|  | QUOTE_0_TERMINATED); | 
|  | } else { | 
|  | tprints("???"); | 
|  | tprints_comment("misplaced string"); | 
|  | } | 
|  | } | 
|  |  | 
|  | static inline bool | 
|  | dm_ioctl_has_params(const unsigned int code) | 
|  | { | 
|  | switch (code) { | 
|  | case DM_VERSION: | 
|  | case DM_REMOVE_ALL: | 
|  | case DM_DEV_CREATE: | 
|  | case DM_DEV_REMOVE: | 
|  | case DM_DEV_SUSPEND: | 
|  | case DM_DEV_STATUS: | 
|  | case DM_TABLE_CLEAR: | 
|  | case DM_DEV_ARM_POLL: | 
|  | return false; | 
|  | } | 
|  |  | 
|  | return true; | 
|  | } | 
|  |  | 
|  | static int | 
|  | dm_known_ioctl(struct tcb *const tcp, const unsigned int code, | 
|  | const kernel_ulong_t arg) | 
|  | { | 
|  | struct dm_ioctl *ioc = NULL; | 
|  | struct dm_ioctl *entering_ioc = NULL; | 
|  | bool ioc_changed = false; | 
|  |  | 
|  | if (entering(tcp)) { | 
|  | ioc = malloc(sizeof(*ioc)); | 
|  | if (!ioc) | 
|  | return 0; | 
|  | } else { | 
|  | ioc = alloca(sizeof(*ioc)); | 
|  | } | 
|  |  | 
|  | if ((umoven(tcp, arg, offsetof(struct dm_ioctl, data), ioc) < 0) || | 
|  | (ioc->data_size < offsetof(struct dm_ioctl, data_size))) { | 
|  | if (entering(tcp)) | 
|  | free(ioc); | 
|  | return 0; | 
|  | } | 
|  | if (entering(tcp)) | 
|  | set_tcb_priv_data(tcp, ioc, free); | 
|  | else { | 
|  | entering_ioc = get_tcb_priv_data(tcp); | 
|  |  | 
|  | /* | 
|  | * retrieve_status, __dev_status called only in case of success, | 
|  | * so it looks like there's no need to check open_count, | 
|  | * event_nr, target_count, dev fields for change (they are | 
|  | * printed only in case of absence of errors). | 
|  | */ | 
|  | if (!entering_ioc || | 
|  | (ioc->version[0] != entering_ioc->version[0]) || | 
|  | (ioc->version[1] != entering_ioc->version[1]) || | 
|  | (ioc->version[2] != entering_ioc->version[2]) || | 
|  | (ioc->data_size != entering_ioc->data_size) || | 
|  | (ioc->data_start != entering_ioc->data_start) || | 
|  | (ioc->flags != entering_ioc->flags)) | 
|  | ioc_changed = true; | 
|  | } | 
|  |  | 
|  | if (exiting(tcp) && syserror(tcp) && !ioc_changed) | 
|  | return RVAL_IOCTL_DECODED; | 
|  |  | 
|  | /* | 
|  | * device mapper code uses %d in some places and %u in another, but | 
|  | * fields themselves are declared as __u32. | 
|  | */ | 
|  | tprintf("%s{version=%u.%u.%u",  entering(tcp) ? ", " : " => ", | 
|  | ioc->version[0], ioc->version[1], ioc->version[2]); | 
|  | /* | 
|  | * if we use a different version of ABI, do not attempt to decode | 
|  | * ioctl fields | 
|  | */ | 
|  | if (ioc->version[0] != DM_VERSION_MAJOR) { | 
|  | tprints_comment("unsupported device mapper ABI version"); | 
|  | goto skip; | 
|  | } | 
|  |  | 
|  | PRINT_FIELD_U(", ", *ioc, data_size); | 
|  |  | 
|  | if (ioc->data_size < offsetof(struct dm_ioctl, data)) { | 
|  | tprints_comment("data_size too small"); | 
|  | goto skip; | 
|  | } | 
|  |  | 
|  | if (dm_ioctl_has_params(code)) | 
|  | PRINT_FIELD_U(", ", *ioc, data_start); | 
|  |  | 
|  | dm_decode_device(code, ioc); | 
|  | dm_decode_values(tcp, code, ioc); | 
|  | dm_decode_flags(ioc); | 
|  |  | 
|  | switch (code) { | 
|  | case DM_DEV_WAIT: | 
|  | case DM_TABLE_STATUS: | 
|  | if (entering(tcp) || syserror(tcp)) | 
|  | break; | 
|  | dm_decode_dm_target_spec(tcp, arg, ioc); | 
|  | break; | 
|  | case DM_TABLE_LOAD: | 
|  | if (exiting(tcp)) | 
|  | break; | 
|  | dm_decode_dm_target_spec(tcp, arg, ioc); | 
|  | break; | 
|  | case DM_TABLE_DEPS: | 
|  | if (entering(tcp) || syserror(tcp)) | 
|  | break; | 
|  | dm_decode_dm_target_deps(tcp, arg, ioc); | 
|  | break; | 
|  | case DM_LIST_DEVICES: | 
|  | if (entering(tcp) || syserror(tcp)) | 
|  | break; | 
|  | dm_decode_dm_name_list(tcp, arg, ioc); | 
|  | break; | 
|  | case DM_LIST_VERSIONS: | 
|  | if (entering(tcp) || syserror(tcp)) | 
|  | break; | 
|  | dm_decode_dm_target_versions(tcp, arg, ioc); | 
|  | break; | 
|  | case DM_TARGET_MSG: | 
|  | if (entering(tcp)) | 
|  | dm_decode_dm_target_msg(tcp, arg, ioc); | 
|  | else if (!syserror(tcp) && ioc->flags & DM_DATA_OUT_FLAG) | 
|  | dm_decode_string(tcp, arg, ioc); | 
|  | break; | 
|  | case DM_DEV_RENAME: | 
|  | case DM_DEV_SET_GEOMETRY: | 
|  | if (exiting(tcp)) | 
|  | break; | 
|  | dm_decode_string(tcp, arg, ioc); | 
|  | break; | 
|  | } | 
|  |  | 
|  | skip: | 
|  | tprints("}"); | 
|  | return entering(tcp) ? 0 : RVAL_IOCTL_DECODED; | 
|  | } | 
|  |  | 
|  | int | 
|  | dm_ioctl(struct tcb *const tcp, const unsigned int code, const kernel_ulong_t arg) | 
|  | { | 
|  | switch (code) { | 
|  | case DM_VERSION: | 
|  | case DM_REMOVE_ALL: | 
|  | case DM_LIST_DEVICES: | 
|  | case DM_DEV_CREATE: | 
|  | case DM_DEV_REMOVE: | 
|  | case DM_DEV_RENAME: | 
|  | case DM_DEV_SUSPEND: | 
|  | case DM_DEV_STATUS: | 
|  | case DM_DEV_WAIT: | 
|  | case DM_TABLE_LOAD: | 
|  | case DM_TABLE_CLEAR: | 
|  | case DM_TABLE_DEPS: | 
|  | case DM_TABLE_STATUS: | 
|  | case DM_LIST_VERSIONS: | 
|  | case DM_TARGET_MSG: | 
|  | case DM_DEV_SET_GEOMETRY: | 
|  | case DM_DEV_ARM_POLL: | 
|  | return dm_known_ioctl(tcp, code, arg); | 
|  | default: | 
|  | return RVAL_DECODED; | 
|  | } | 
|  | } | 
|  |  | 
|  | # else /* !(DM_VERSION_MAJOR == 4) */ | 
|  |  | 
|  | int | 
|  | dm_ioctl(struct tcb *const tcp, const unsigned int code, const kernel_ulong_t arg) | 
|  | { | 
|  | return RVAL_DECODED; | 
|  | } | 
|  |  | 
|  | # endif /* DM_VERSION_MAJOR == 4 */ | 
|  | #endif /* HAVE_LINUX_DM_IOCTL_H */ |