From a507a0bb777c552e43e1e45f302703a09ffea1b8 Mon Sep 17 00:00:00 2001 From: Mikulas Patocka Date: Sun, 2 Oct 2016 17:59:06 -0400 Subject: [PATCH] Add support for decoding of DM_* ioctl commands * dm.c: New file. * xlat/dm_flags.in: Likewise. * Makefile.am (strace_SOURCES): Add dm.c. * configure.ac (AC_CHECK_HEADERS): Add linux/dm-ioctl.h. * defs.h (dm_ioctl): New prototype. * ioctl.c (ioctl_decode) [HAVE_LINUX_DM_IOCTL_H]: Call dm_ioctl for 0xfd ioctl type. * tests/ioctl_dm.c: New file. * tests/ioctl_dm-v.c: Likewise. * tests/ioctl_dm.test: New test. * tests/ioctl_dm-v.test: Likewise. * tests/.gitignore: Add ioctl_dm and ioctl_dm-v. * tests/Makefile.am (check_PROGRAMS): Likewise. (DECODER_TESTS): Add ioctl_dm.test and ioctl_dm-v.test. Co-authored-by: Eugene Syromyatnikov --- Makefile.am | 1 + configure.ac | 1 + defs.h | 1 + dm.c | 553 ++++++++++++++++++++++++++++++ ioctl.c | 4 + tests/.gitignore | 2 + tests/Makefile.am | 4 + tests/ioctl_dm-v.c | 2 + tests/ioctl_dm-v.test | 12 + tests/ioctl_dm.c | 757 ++++++++++++++++++++++++++++++++++++++++++ tests/ioctl_dm.test | 12 + xlat/dm_flags.in | 19 ++ 12 files changed, 1368 insertions(+) create mode 100644 dm.c create mode 100644 tests/ioctl_dm-v.c create mode 100755 tests/ioctl_dm-v.test create mode 100644 tests/ioctl_dm.c create mode 100755 tests/ioctl_dm.test create mode 100644 xlat/dm_flags.in diff --git a/Makefile.am b/Makefile.am index 1c7fd20c..eabed469 100644 --- a/Makefile.am +++ b/Makefile.am @@ -97,6 +97,7 @@ strace_SOURCES = \ desc.c \ dirent.c \ dirent64.c \ + dm.c \ empty.h \ epoll.c \ evdev.c \ diff --git a/configure.ac b/configure.ac index 3102a9b2..61dcf158 100644 --- a/configure.ac +++ b/configure.ac @@ -378,6 +378,7 @@ AC_CHECK_HEADERS(m4_normalize([ elf.h inttypes.h linux/bsg.h + linux/dm-ioctl.h linux/dqblk_xfs.h linux/falloc.h linux/fiemap.h diff --git a/defs.h b/defs.h index 52a9e4d7..1d989b2a 100644 --- a/defs.h +++ b/defs.h @@ -648,6 +648,7 @@ extern void print_struct_statfs64(struct tcb *tcp, long, unsigned long); extern void print_ifindex(unsigned int); +extern int dm_ioctl(struct tcb *, const unsigned int, long); extern int file_ioctl(struct tcb *, const unsigned int, long); extern int fs_x_ioctl(struct tcb *, const unsigned int, long); extern int loop_ioctl(struct tcb *, const unsigned int, long); diff --git a/dm.c b/dm.c new file mode 100644 index 00000000..a72f1075 --- /dev/null +++ b/dm.c @@ -0,0 +1,553 @@ +#include "defs.h" + +#ifdef HAVE_LINUX_DM_IOCTL_H + +# include +# include + +# include + +# if DM_VERSION_MAJOR == 4 + +/* Definitions for command which have been added later */ + +# ifndef DM_LIST_VERSIONS +# define DM_LIST_VERSIONS _IOWR(DM_IOCTL, 0xd, struct dm_ioctl) +# endif +# ifndef DM_TARGET_MSG +# define DM_TARGET_MSG _IOWR(DM_IOCTL, 0xe, struct dm_ioctl) +# endif +# ifndef DM_DEV_SET_GEOMETRY +# define DM_DEV_SET_GEOMETRY _IOWR(DM_IOCTL, 0xf, 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) + tprintf(", dev=makedev(%u, %u)", + major(ioc->dev), minor(ioc->dev)); + if (ioc->name[0]) { + tprints(", name="); + print_quoted_string(ioc->name, DM_NAME_LEN, + QUOTE_0_TERMINATED); + } + if (ioc->uuid[0]) { + tprints(", uuid="); + print_quoted_string(ioc->uuid, DM_UUID_LEN, + QUOTE_0_TERMINATED); + } + 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: + tprintf(", target_count=%" PRIu32, + ioc->target_count); + break; + case DM_DEV_SUSPEND: + if (ioc->flags & DM_SUSPEND_FLAG) + break; + /* Fall through */ + case DM_DEV_RENAME: + case DM_DEV_REMOVE: + case DM_DEV_WAIT: + tprintf(", event_nr=%" PRIu32, + 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: + tprintf(", target_count=%" PRIu32, + ioc->target_count); + tprintf(", open_count=%" PRIu32, + ioc->open_count); + tprintf(", event_nr=%" PRIu32, + ioc->event_nr); + break; + } + } +} + +#include "xlat/dm_flags.h" + +static void +dm_decode_flags(const struct dm_ioctl *ioc) +{ + tprints(", flags="); + printflags(dm_flags, ioc->flags, "DM_???"); +} + +static void +dm_decode_dm_target_spec(struct tcb *tcp, unsigned long addr, + const struct dm_ioctl *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; + + if (abbrev(tcp)) { + if (ioc->target_count) + tprints(", ..."); + + return; + } + + for (i = 0; i < ioc->target_count; i++) { + struct dm_target_spec s; + + offset_end = offset + target_spec_size; + + if (offset_end <= offset || offset_end > ioc->data_size) + goto misplaced; + + tprints(", "); + + if (i >= max_strlen) { + tprints("..."); + break; + } + + if (umove_or_printaddr(tcp, addr + offset, &s)) + break; + + tprintf("{sector_start=%" PRI__u64 ", length=%" PRI__u64, + s.sector_start, s.length); + + if (exiting(tcp)) + tprintf(", status=%" PRId32, s.status); + + tprints(", target_type="); + print_quoted_string(s.target_type, DM_MAX_TYPE_NAME, + QUOTE_0_TERMINATED); + + tprints(", string="); + printstr_ex(tcp, addr + offset_end, ioc->data_size - offset_end, + QUOTE_0_TERMINATED); + tprintf("}"); + + if (entering(tcp)) + offset += s.next; + else + offset = ioc->data_start + s.next; + + if (offset <= offset_end) + goto misplaced; + } + + return; + +misplaced: + tprints(", /* 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; + + tprintf("makedev(%u, %u)", major(*dev), minor(*dev)); + + return 1; +} + +static void +dm_decode_dm_target_deps(struct tcb *tcp, unsigned long addr, + const struct dm_ioctl *ioc) +{ + 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 (abbrev(tcp)) { + tprints(", ..."); + return; + } + + tprints(", "); + + 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; + + tprintf("{count=%u, deps=", s.count); + + print_array(tcp, addr + offset_end, s.count, &dev_buf, sizeof(dev_buf), + umoven_or_printaddr, dm_print_dev, NULL); + + tprints("}"); + + return; + +misplaced: + tprints("/* misplaced struct dm_target_deps */ ..."); +} + +static void +dm_decode_dm_name_list(struct tcb *tcp, unsigned long addr, + const struct dm_ioctl *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; + uint32_t count; + + if (abbrev(tcp)) { + tprints(", ..."); + return; + } + + for (count = 0;; count++) { + offset_end = offset + name_list_name_offs; + + if (offset_end <= offset || offset_end > ioc->data_size) + goto misplaced; + + tprints(", "); + + if (count >= max_strlen) { + tprints("..."); + break; + } + + if (umove_or_printaddr(tcp, addr + offset, &s)) + break; + if (!count && !s.dev) { + tprints("/* no devices present */"); + break; + } + + tprintf("{dev=makedev(%u, %u), name=", major(s.dev), + minor(s.dev)); + printstr_ex(tcp, addr + offset_end, ioc->data_size - offset_end, + QUOTE_0_TERMINATED); + tprints("}"); + + if (!s.next) + break; + + offset += s.next; + if (offset <= offset_end) + goto misplaced; + } + + return; + +misplaced: + tprints(", /* misplaced struct dm_name_list */ ..."); +} + +static void +dm_decode_dm_target_versions(struct tcb *tcp, unsigned long addr, + const struct dm_ioctl *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; + uint32_t count; + + if (abbrev(tcp)) { + tprints(", ..."); + return; + } + + for (count = 0;; count++) { + offset_end = offset + target_vers_name_offs; + + if (offset_end <= offset || offset_end > ioc->data_size) + goto misplaced; + + tprints(", "); + + 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; + if (offset <= offset_end) + goto misplaced; + } + + return; + +misplaced: + tprints(", /* misplaced struct dm_target_versions */ ..."); +} + +static void +dm_decode_dm_target_msg(struct tcb *tcp, unsigned long addr, + const struct dm_ioctl *ioc) +{ + 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 (abbrev(tcp)) { + tprints(", ..."); + return; + } + + if (offset_end > offset && offset_end <= ioc->data_size) { + struct dm_target_msg s; + + tprints(", "); + + if (umove_or_printaddr(tcp, addr + offset, &s)) + return; + + tprintf("{sector=%" PRI__u64 ", message=", s.sector); + printstr_ex(tcp, addr + offset_end, ioc->data_size - offset_end, + QUOTE_0_TERMINATED); + tprints("}"); + } else { + tprints(", /* misplaced struct dm_target_msg */"); + } +} + +static void +dm_decode_string(struct tcb *tcp, unsigned long addr, + const struct dm_ioctl *ioc) +{ + uint32_t offset = ioc->data_start; + + if (abbrev(tcp)) { + tprints(", ..."); + return; + } + + if (offset < ioc->data_size) { + tprints(", string="); + printstr_ex(tcp, addr + offset, ioc->data_size - offset, + QUOTE_0_TERMINATED); + } else { + tprints(", /* 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: + return false; + } + + return true; +} + +static int +dm_known_ioctl(struct tcb *tcp, const unsigned int code, long 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 1; + + /* + * 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(", /* Unsupported device mapper ABI version */ ..."); + goto skip; + } + + tprintf(", data_size=%u", ioc->data_size); + + if (dm_ioctl_has_params(code)) + tprintf(", data_start=%u", ioc->data_start); + + if (ioc->data_size < offsetof(struct dm_ioctl, data)) { + tprints(", /* Incorrect data_size */ ..."); + goto skip; + } + + 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 1; +} + +int +dm_ioctl(struct tcb *tcp, const unsigned int code, long 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: + return dm_known_ioctl(tcp, code, arg); + default: + return 0; + } +} + +# else /* !(DM_VERSION_MAJOR == 4) */ + +int +dm_ioctl(struct tcb *tcp, const unsigned int code, long arg) +{ + return 0; +} + +# endif /* DM_VERSION_MAJOR == 4 */ +#endif /* HAVE_LINUX_DM_IOCTL_H */ diff --git a/ioctl.c b/ioctl.c index e4b20d97..e4fd59cb 100644 --- a/ioctl.c +++ b/ioctl.c @@ -281,6 +281,10 @@ ioctl_decode(struct tcb *tcp) #ifdef HAVE_LINUX_BTRFS_H case 0x94: return btrfs_ioctl(tcp, code, arg); +#endif +#ifdef HAVE_LINUX_DM_IOCTL_H + case 0xfd: + return dm_ioctl(tcp, code, arg); #endif default: break; diff --git a/tests/.gitignore b/tests/.gitignore index 05f1512f..ef1e94b9 100644 --- a/tests/.gitignore +++ b/tests/.gitignore @@ -110,6 +110,8 @@ inotify inotify_init1 ioctl ioctl_block +ioctl_dm +ioctl_dm-v ioctl_evdev ioctl_evdev-v ioctl_mtd diff --git a/tests/Makefile.am b/tests/Makefile.am index e1da8a54..f83cccc7 100644 --- a/tests/Makefile.am +++ b/tests/Makefile.am @@ -170,6 +170,8 @@ check_PROGRAMS = \ inotify_init1 \ ioctl \ ioctl_block \ + ioctl_dm \ + ioctl_dm-v \ ioctl_evdev \ ioctl_evdev-v \ ioctl_mtd \ @@ -542,6 +544,8 @@ DECODER_TESTS = \ inotify_init1.test \ ioctl.test \ ioctl_block.test \ + ioctl_dm.test \ + ioctl_dm-v.test \ ioctl_evdev.test \ ioctl_evdev-v.test \ ioctl_mtd.test \ diff --git a/tests/ioctl_dm-v.c b/tests/ioctl_dm-v.c new file mode 100644 index 00000000..d95058f2 --- /dev/null +++ b/tests/ioctl_dm-v.c @@ -0,0 +1,2 @@ +#define VERBOSE 1 +#include "ioctl_dm.c" diff --git a/tests/ioctl_dm-v.test b/tests/ioctl_dm-v.test new file mode 100755 index 00000000..ade3b9e8 --- /dev/null +++ b/tests/ioctl_dm-v.test @@ -0,0 +1,12 @@ +#!/bin/sh + +# Check unabbreviated decoding of DM* ioctls. + +. "${srcdir=.}/init.sh" + +run_prog > /dev/null +run_strace -a16 -s9 -veioctl $args > "$EXP" +check_prog grep +grep -v '^ioctl([012],' < "$LOG" > "$OUT" +match_diff "$OUT" "$EXP" +rm -f "$EXP" "$OUT" diff --git a/tests/ioctl_dm.c b/tests/ioctl_dm.c new file mode 100644 index 00000000..718bc890 --- /dev/null +++ b/tests/ioctl_dm.c @@ -0,0 +1,757 @@ +/* + * Check decoding of DM_* commands of ioctl syscall. + * + * Copyright (c) 2016 Mikulas Patocka + * Copyright (c) 2016 Eugene Syromyatnikov + * 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 "tests.h" + +#ifdef HAVE_LINUX_DM_IOCTL_H + +# include +# include +# include +# include +# include +# include +# include +# include + +# ifndef VERBOSE +# define VERBOSE 0 +# endif + +# define STR32 "AbCdEfGhIjKlMnOpQrStUvWxYz012345" + +# define ALIGNED_SIZE(s_, t_) \ + (((s_) + (ALIGNOF(t_) - 1UL)) & ~(ALIGNOF(t_) - 1UL)) +# define ALIGNED_OFFSET(t_, m_) \ + ALIGNED_SIZE(offsetof(t_, m_), t_) + +static const char str129[] = STR32 STR32 STR32 STR32 "6"; + +static const __u64 dts_sector_base = (__u64) 0xdeadca75facef157ULL; +static const __u64 dts_sector_step = (__u64) 0x100000001ULL; +static const __u64 dts_length_base = (__u64) 0xbadc0dedda7a1057ULL; +static const __u64 dts_length_step = (__u64) 0x700000007ULL; +static const __s32 dts_status_base = (__s32) 3141592653U; +static const __s32 dts_status_step = 0x1234; + +static const size_t min_sizeof_dm_ioctl = + offsetof(struct dm_ioctl, data); + +static struct s { + struct dm_ioctl ioc; + union { + struct { + struct dm_target_spec target_spec; + char target_params[256]; + } ts; + struct { + struct dm_target_msg target_msg; + char target_string[256]; + } tm; + char string[256]; + } u; +} s; + +struct dm_table_open_test { + struct dm_ioctl ioc; + struct dm_target_spec target0; + char param0[1]; + struct dm_target_spec target1; + char param1[2]; + struct dm_target_spec target2; + char param2[3]; + struct dm_target_spec target3; + char param3[4]; + struct dm_target_spec target4; + char param4[5]; + struct dm_target_spec target5; + char param5[6]; + struct dm_target_spec target6; + char param6[7]; + struct dm_target_spec target7; + char param7[8]; + struct dm_target_spec target8; + char param8[9]; + struct dm_target_spec target9; + char param9[10]; +}; + +struct dm_target_msg_test { + struct dm_ioctl ioc; + struct dm_target_msg msg; +}; + +struct args { + unsigned int arg; + const char *str; + bool has_params; + bool has_event_nr; +}; + + +static void +init_s(struct dm_ioctl *s, size_t size, size_t offs) +{ + memset(s, 0, size); + s->version[0] = DM_VERSION_MAJOR; + s->version[1] = 1; + s->version[2] = 2; + s->data_size = size; + s->data_start = offs; + s->dev = 0x1234; + strcpy(s->name, "nnn"); + strcpy(s->uuid, "uuu"); +} + +static void +init_dm_target_spec(struct dm_target_spec *ptr, uint32_t id) +{ + ptr->sector_start = dts_sector_base + dts_sector_step * id; + ptr->length = dts_length_base + dts_length_step * id; + ptr->status = dts_status_base + dts_status_step * id; + + strncpy(ptr->target_type, str129 + + id % (sizeof(str129) - sizeof(ptr->target_type)), + id % (sizeof(ptr->target_type) + 1)); + if (id % (sizeof(ptr->target_type) + 1) < sizeof(ptr->target_type)) + ptr->target_type[id % (sizeof(ptr->target_type) + 1)] = '\0'; +} + +# if VERBOSE +static void +print_dm_target_spec(struct dm_target_spec *ptr, uint32_t id) +{ + printf("{sector_start=%" PRI__u64 ", length=%" PRI__u64 ", " + "target_type=\"%.*s\", string=", + dts_sector_base + dts_sector_step * id, + dts_length_base + dts_length_step * id, + (int) (id % (sizeof(ptr->target_type) + 1)), + str129 + id % (sizeof(str129) - sizeof(ptr->target_type))); +} +# endif /* VERBOSE */ + +int +main(void) +{ + /* We can't check these properly for now */ + static struct args dummy_check_cmds_nodev[] = { + { ARG_STR(DM_REMOVE_ALL), false }, + { ARG_STR(DM_LIST_DEVICES), true }, + { ARG_STR(DM_LIST_VERSIONS), true }, + }; + static struct args dummy_check_cmds[] = { + { ARG_STR(DM_DEV_CREATE), false }, + { ARG_STR(DM_DEV_REMOVE), false, true }, + { ARG_STR(DM_DEV_STATUS), false }, + { ARG_STR(DM_DEV_WAIT), true, true }, + { ARG_STR(DM_TABLE_CLEAR), false }, + { ARG_STR(DM_TABLE_DEPS), true }, + { ARG_STR(DM_TABLE_STATUS), true }, + }; + + struct dm_ioctl *unaligned_dm_arg = + tail_alloc(offsetof(struct dm_ioctl, data)); + struct dm_ioctl *dm_arg = + tail_alloc(ALIGNED_OFFSET(struct dm_ioctl, data)); + struct dm_table_open_test *dm_arg_open1 = + tail_alloc(ALIGNED_OFFSET(struct dm_table_open_test, target1)); + struct dm_table_open_test *dm_arg_open2 = + tail_alloc(ALIGNED_OFFSET(struct dm_table_open_test, param1)); + struct dm_table_open_test *dm_arg_open3 = + tail_alloc(ALIGNED_OFFSET(struct dm_table_open_test, target9)); + struct dm_target_msg_test *dm_arg_msg = + tail_alloc(sizeof(*dm_arg_msg)); + + long rc; + const char *errstr; + unsigned int i; + + + /* Incorrect operation */ + ioctl(-1, _IOW(DM_IOCTL, 0xde, int), dm_arg); + printf("ioctl(-1, _IOC(_IOC_WRITE, %#04x, 0xde, %#04zx), %p) = " + "-1 EBADF (%m)\n", + DM_IOCTL, sizeof(int), dm_arg); + + + /* DM_VERSION */ + /* Incorrect pointer */ + ioctl(-1, DM_VERSION, dm_arg + 1); + printf("ioctl(-1, DM_VERSION, %p) = -1 EBADF (%m)\n", dm_arg + 1); + + /* Incorrect data_size */ + init_s(dm_arg, 0, 0); + ioctl(-1, DM_VERSION, &s); + printf("ioctl(-1, DM_VERSION, %p) = -1 EBADF (%m)\n", &s); + + /* Incorrect version */ + init_s(dm_arg, min_sizeof_dm_ioctl, 0); + dm_arg->version[0] = 0xbadc0ded; + dm_arg->version[1] = 0xbadc0dee; + dm_arg->version[2] = 0xbadc0def; + ioctl(-1, DM_VERSION, dm_arg); + printf("ioctl(-1, DM_VERSION, {version=%u.%u.%u, " + "/* Unsupported device mapper ABI version */ ...}) = " + "-1 EBADF (%m)\n", 0xbadc0ded, 0xbadc0dee, 0xbadc0def); + + /* Incorrect data_size */ + init_s(dm_arg, 14, 64); + ioctl(-1, DM_VERSION, dm_arg); + printf("ioctl(-1, DM_VERSION, {version=4.1.2, data_size=14, " + "/* Incorrect data_size */ ...}) = -1 EBADF (%m)\n"); + + /* Unterminated name/uuid */ + init_s(dm_arg, min_sizeof_dm_ioctl, 0); + strncpy(dm_arg->name, str129, sizeof(dm_arg->name)); + strncpy(dm_arg->uuid, str129, sizeof(dm_arg->uuid)); + ioctl(-1, DM_VERSION, dm_arg); + printf("ioctl(-1, DM_VERSION, {version=4.1.2, data_size=%zu, " + "dev=makedev(18, 52), name=\"%.127s\", uuid=\"%.128s\", " + "flags=0}) = -1 EBADF (%m)\n", + min_sizeof_dm_ioctl, str129, str129); + + /* Normal call */ + init_s(dm_arg, min_sizeof_dm_ioctl, 0); + ioctl(-1, DM_VERSION, dm_arg); + printf("ioctl(-1, DM_VERSION, " + "{version=4.1.2, data_size=%zu, " + "dev=makedev(18, 52), name=\"nnn\", uuid=\"uuu\", flags=0}) = " + "-1 EBADF (%m)\n", min_sizeof_dm_ioctl); + + /* Zero dev, name, uuid */ + init_s(dm_arg, min_sizeof_dm_ioctl, 0); + dm_arg->data_size = 0xfacefeed; + dm_arg->dev = 0; + dm_arg->name[0] = '\0'; + dm_arg->uuid[0] = '\0'; + ioctl(-1, DM_VERSION, dm_arg); + printf("ioctl(-1, DM_VERSION, " + "{version=4.1.2, data_size=%u, flags=0}) = " + "-1 EBADF (%m)\n", 0xfacefeed); + + /* Flag */ + init_s(dm_arg, min_sizeof_dm_ioctl, 0); + dm_arg->flags = 0xffffffff; + ioctl(-1, DM_VERSION, dm_arg); + printf("ioctl(-1, DM_VERSION, " + "{version=4.1.2, data_size=%zu, " + "dev=makedev(18, 52), name=\"nnn\", uuid=\"uuu\", flags=" + "DM_READONLY_FLAG|DM_SUSPEND_FLAG|DM_EXISTS_FLAG|" + "DM_PERSISTENT_DEV_FLAG|DM_STATUS_TABLE_FLAG|" + "DM_ACTIVE_PRESENT_FLAG|DM_INACTIVE_PRESENT_FLAG|" + "DM_BUFFER_FULL_FLAG|DM_SKIP_BDGET_FLAG|DM_SKIP_LOCKFS_FLAG|" + "DM_NOFLUSH_FLAG|DM_QUERY_INACTIVE_TABLE_FLAG|" + "DM_UEVENT_GENERATED_FLAG|DM_UUID_FLAG|DM_SECURE_DATA_FLAG|" + "DM_DATA_OUT_FLAG|DM_DEFERRED_REMOVE|DM_INTERNAL_SUSPEND_FLAG|" + "0xfff80080}) = -1 EBADF (%m)\n", + min_sizeof_dm_ioctl); + + /* Normal call */ + init_s(&s.ioc, sizeof(s.ioc), 0); + ioctl(-1, DM_VERSION, &s); + printf("ioctl(-1, DM_VERSION, " + "{version=4.1.2, data_size=%zu, " + "dev=makedev(18, 52), name=\"nnn\", uuid=\"uuu\", flags=0}) = " + "-1 EBADF (%m)\n", sizeof(s.ioc)); + + + /* DM_REMOVE_ALL */ + /* DM_LIST_DEVICES */ + /* DM_LIST_VERSIONS */ + for (i = 0; i < ARRAY_SIZE(dummy_check_cmds_nodev); i++) { + init_s(dm_arg, min_sizeof_dm_ioctl, 0); + ioctl(-1, dummy_check_cmds_nodev[i].arg, dm_arg); + printf("ioctl(-1, %s, {version=4.1.2, data_size=%zu%s, " + "flags=0}) = -1 EBADF (%m)\n", + dummy_check_cmds_nodev[i].str, + min_sizeof_dm_ioctl, + dummy_check_cmds_nodev[i].has_params ? + ", data_start=0" : ""); + } + + + /* DM_DEV_CREATE */ + /* DM_DEV_REMOVE */ + /* DM_DEV_STATUS */ + /* DM_DEV_WAIT */ + /* DM_TABLE_CLEAR */ + /* DM_TABLE_DEPS */ + /* DM_TABLE_STATUS */ + for (i = 0; i < ARRAY_SIZE(dummy_check_cmds); i++) { + init_s(dm_arg, min_sizeof_dm_ioctl, 0); + ioctl(-1, dummy_check_cmds[i].arg, dm_arg); + printf("ioctl(-1, %s, {version=4.1.2, data_size=%zu%s, " + "dev=makedev(18, 52), name=\"nnn\", uuid=\"uuu\"%s, " + "flags=0}) = -1 EBADF (%m)\n", + dummy_check_cmds[i].str, min_sizeof_dm_ioctl, + dummy_check_cmds[i].has_params ? ", data_start=0" : "", + dummy_check_cmds[i].has_event_nr ? ", event_nr=0" : ""); + } + + + /* DM_DEV_SUSPEND */ + init_s(&s.ioc, sizeof(s.ioc), 0); + s.ioc.flags = DM_SUSPEND_FLAG; + s.ioc.event_nr = 0xbadc0ded; + ioctl(-1, DM_DEV_SUSPEND, &s); + printf("ioctl(-1, DM_DEV_SUSPEND, " + "{version=4.1.2, data_size=%zu, " + "dev=makedev(18, 52), name=\"nnn\", uuid=\"uuu\", " + "flags=DM_SUSPEND_FLAG}) = -1 EBADF (%m)\n", sizeof(s.ioc)); + + init_s(&s.ioc, sizeof(s.ioc), 0); + s.ioc.event_nr = 0xbadc0ded; + ioctl(-1, DM_DEV_SUSPEND, &s); + printf("ioctl(-1, DM_DEV_SUSPEND, " + "{version=4.1.2, data_size=%zu, dev=makedev(18, 52), " + "name=\"nnn\", uuid=\"uuu\", event_nr=3134983661, " + "flags=0}) = -1 EBADF (%m)\n", sizeof(s.ioc)); + + + /* DM_TABLE_LOAD */ + init_s(&s.ioc, sizeof(s), offsetof(struct s, u)); + s.ioc.target_count = 1; + s.u.ts.target_spec.sector_start = 0x10; + s.u.ts.target_spec.length = 0x20; + s.u.ts.target_spec.next = + sizeof(s.u.ts.target_spec) + sizeof(s.u.ts.target_params); + strcpy(s.u.ts.target_spec.target_type, "tgt"); + strcpy(s.u.ts.target_params, "tparams"); + ioctl(-1, DM_TABLE_LOAD, &s); + printf("ioctl(-1, DM_TABLE_LOAD, " + "{version=4.1.2, data_size=%u, data_start=%u, " + "dev=makedev(18, 52), name=\"nnn\", uuid=\"uuu\", " + "target_count=1, flags=0, " +# if VERBOSE + "{sector_start=16, length=32, target_type=\"tgt\", " + "string=\"tparams\"}" +# else /* !VERBOSE */ + "..." +# endif /* VERBOSE */ + "}) = -1 EBADF (%m)\n", s.ioc.data_size, s.ioc.data_start); + + /* No targets */ + init_s(dm_arg, min_sizeof_dm_ioctl, min_sizeof_dm_ioctl); + dm_arg->data_size = sizeof(*dm_arg); + dm_arg->target_count = 0; + ioctl(-1, DM_TABLE_LOAD, dm_arg); + printf("ioctl(-1, DM_TABLE_LOAD, " + "{version=4.1.2, data_size=%zu, data_start=%zu, " + "dev=makedev(18, 52), name=\"nnn\", uuid=\"uuu\", " + "target_count=0, flags=0}) = -1 EBADF (%m)\n", + sizeof(*dm_arg), min_sizeof_dm_ioctl); + + /* Invalid data_start */ + init_s(dm_arg, min_sizeof_dm_ioctl, 0xfffffff8); + dm_arg->data_size = sizeof(*dm_arg); + dm_arg->target_count = 1234; + ioctl(-1, DM_TABLE_LOAD, dm_arg); + printf("ioctl(-1, DM_TABLE_LOAD, " + "{version=4.1.2, data_size=%zu, data_start=%u, " + "dev=makedev(18, 52), name=\"nnn\", uuid=\"uuu\", " + "target_count=1234, flags=0, " +# if VERBOSE + "/* misplaced struct dm_target_spec */ ..." +# else /* !VERBOSE */ + "..." +# endif /* VERBOSE */ + "}) = -1 EBADF (%m)\n", sizeof(*dm_arg), 0xfffffff8); + + /* Inaccessible pointer */ + init_s(&dm_arg_open1->ioc, offsetof(struct dm_table_open_test, target1), + offsetof(struct dm_table_open_test, target1)); + dm_arg_open1->ioc.data_size = sizeof(*dm_arg_open1); + dm_arg_open1->ioc.target_count = 0xdeaddea1; + ioctl(-1, DM_TABLE_LOAD, dm_arg_open1); + printf("ioctl(-1, DM_TABLE_LOAD, " + "{version=4.1.2, data_size=%zu, data_start=%zu, " + "dev=makedev(18, 52), name=\"nnn\", uuid=\"uuu\", " + "target_count=3735936673, flags=0, " +# if VERBOSE + "%p" +# else /* !VERBOSE */ + "..." +# endif /* VERBOSE */ + "}) = -1 EBADF (%m)\n", sizeof(*dm_arg_open1), + offsetof(struct dm_table_open_test, target1) +# if VERBOSE + , (char *) dm_arg_open1 + + offsetof(struct dm_table_open_test, target1) +# endif /* VERBOSE */ + ); + + /* Inaccessible string */ + init_s(&dm_arg_open2->ioc, offsetof(struct dm_table_open_test, param1), + offsetof(struct dm_table_open_test, target1)); + dm_arg_open2->ioc.data_size = sizeof(*dm_arg_open2); + dm_arg_open2->ioc.target_count = 2; + init_dm_target_spec(&dm_arg_open2->target1, 7); + dm_arg_open2->target1.next = + offsetof(struct dm_table_open_test, target3) - + offsetof(struct dm_table_open_test, target1); + rc = ioctl(-1, DM_TABLE_LOAD, dm_arg_open2); + errstr = sprintrc(rc); + printf("ioctl(-1, DM_TABLE_LOAD, " + "{version=4.1.2, data_size=%zu, data_start=%zu, " + "dev=makedev(18, 52), name=\"nnn\", uuid=\"uuu\", " + "target_count=2, flags=0, ", + sizeof(*dm_arg_open2), + offsetof(struct dm_table_open_test, target1)); +# if VERBOSE + print_dm_target_spec(&dm_arg_open2->target1, 7); + printf("%p}, %p", + (char *) dm_arg_open2 + + offsetof(struct dm_table_open_test, param1), + (char *) dm_arg_open2 + + offsetof(struct dm_table_open_test, target3)); +# else /* !VERBOSE */ + printf("..."); +# endif /* VERBOSE */ + printf("}) = %s\n", errstr); + + /* Incorrect next */ + init_s(&dm_arg_open3->ioc, offsetof(struct dm_table_open_test, target5), + offsetof(struct dm_table_open_test, target0)); + dm_arg_open3->ioc.target_count = 4; + + init_dm_target_spec(&dm_arg_open3->target0, 9); + dm_arg_open3->target0.next = + offsetof(struct dm_table_open_test, target1) - + offsetof(struct dm_table_open_test, target0); + dm_arg_open3->param0[0] = '\0'; + + init_dm_target_spec(&dm_arg_open3->target1, 15); + dm_arg_open3->target1.next = + offsetof(struct dm_table_open_test, target3) - + offsetof(struct dm_table_open_test, target1); + dm_arg_open3->param1[0] = '\377'; + dm_arg_open3->param1[1] = '\0'; + + init_dm_target_spec(&dm_arg_open3->target3, 42); + dm_arg_open3->target3.next = 0xdeadbeef; + dm_arg_open3->param3[0] = '\1'; + dm_arg_open3->param3[1] = '\2'; + dm_arg_open3->param3[2] = '\0'; + + rc = ioctl(-1, DM_TABLE_LOAD, dm_arg_open3); + errstr = sprintrc(rc); + printf("ioctl(-1, DM_TABLE_LOAD, " + "{version=4.1.2, data_size=%zu, data_start=%zu, " + "dev=makedev(18, 52), name=\"nnn\", uuid=\"uuu\", " + "target_count=4, flags=0, ", + offsetof(struct dm_table_open_test, target5), + offsetof(struct dm_table_open_test, target0)); +# if VERBOSE + print_dm_target_spec(&dm_arg_open3->target0, 9); + printf("\"\"}, "); + print_dm_target_spec(&dm_arg_open3->target1, 15); + printf("\"\\377\"}, "); + print_dm_target_spec(&dm_arg_open3->target1, 42); + printf("\"\\1\\2\"}, /* misplaced struct dm_target_spec */ ..."); +# else /* !VERBOSE */ + printf("..."); +# endif /* VERBOSE */ + printf("}) = %s\n", errstr); + + #define FILL_DM_TARGET(id, id_next) \ + do { \ + init_dm_target_spec(&dm_arg_open3->target##id, id); \ + dm_arg_open3->target##id.next = \ + offsetof(struct dm_table_open_test, \ + target##id_next) - \ + offsetof(struct dm_table_open_test, \ + target##id); \ + strncpy(dm_arg_open3->param##id, str129 + id * 2, id); \ + dm_arg_open3->param##id[id] = '\0'; \ + } while (0) + #define PRINT_DM_TARGET(id) \ + do { \ + print_dm_target_spec(&dm_arg_open3->target##id, id); \ + printf("\"%.*s\"}, ", id, str129 + id * 2); \ + } while (0) + + /* max_strlen limit */ + init_s(&dm_arg_open3->ioc, offsetof(struct dm_table_open_test, target9), + offsetof(struct dm_table_open_test, target0)); + dm_arg_open3->ioc.data_size = sizeof(*dm_arg_open3); + dm_arg_open3->ioc.target_count = 0xbadc0ded; + FILL_DM_TARGET(0, 1); + FILL_DM_TARGET(1, 2); + FILL_DM_TARGET(2, 3); + FILL_DM_TARGET(3, 4); + FILL_DM_TARGET(4, 5); + FILL_DM_TARGET(5, 6); + FILL_DM_TARGET(6, 7); + FILL_DM_TARGET(7, 8); + FILL_DM_TARGET(8, 9); + rc = ioctl(-1, DM_TABLE_LOAD, dm_arg_open3); + errstr = sprintrc(rc); + printf("ioctl(-1, DM_TABLE_LOAD, " + "{version=4.1.2, data_size=%zu, data_start=%zu, " + "dev=makedev(18, 52), name=\"nnn\", uuid=\"uuu\", " + "target_count=3134983661, flags=0, ", + sizeof(*dm_arg_open3), + offsetof(struct dm_table_open_test, target0)); +# if VERBOSE + PRINT_DM_TARGET(0); + PRINT_DM_TARGET(1); + PRINT_DM_TARGET(2); + PRINT_DM_TARGET(3); + PRINT_DM_TARGET(4); + PRINT_DM_TARGET(5); + PRINT_DM_TARGET(6); + PRINT_DM_TARGET(7); + PRINT_DM_TARGET(8); +# endif /* VERBOSE */ + printf("...}) = %s\n", errstr); + + + /* DM_TARGET_MSG */ + init_s(&s.ioc, sizeof(s), offsetof(struct s, u)); + s.u.tm.target_msg.sector = 0x1234; + strcpy(s.u.string + offsetof(struct dm_target_msg, message), + "long target msg"); + ioctl(-1, DM_TARGET_MSG, &s); + printf("ioctl(-1, DM_TARGET_MSG, " + "{version=4.1.2, data_size=%u, data_start=%u, " + "dev=makedev(18, 52), name=\"nnn\", uuid=\"uuu\", flags=0, " +# if VERBOSE + "{sector=4660, message=\"long targ\"...}" +# else /* !VERBOSE */ + "..." +# endif /* VERBOSE */ + "}) = -1 EBADF (%m)\n", + s.ioc.data_size, s.ioc.data_start); + + /* Invalid data_start */ + init_s(dm_arg, min_sizeof_dm_ioctl, min_sizeof_dm_ioctl); + dm_arg->data_size = sizeof(*dm_arg); + ioctl(-1, DM_TARGET_MSG, dm_arg); + printf("ioctl(-1, DM_TARGET_MSG, " + "{version=4.1.2, data_size=%zu, data_start=%zu, " + "dev=makedev(18, 52), name=\"nnn\", uuid=\"uuu\", flags=0, " +# if VERBOSE + "/* misplaced struct dm_target_msg */" +# else /* !VERBOSE */ + "..." +# endif /* VERBOSE */ + "}) = -1 EBADF (%m)\n", + sizeof(*dm_arg), min_sizeof_dm_ioctl); + + /* Invalid data_start */ + init_s(dm_arg, min_sizeof_dm_ioctl, 0xffffffff); + dm_arg->data_size = sizeof(*dm_arg); + ioctl(-1, DM_TARGET_MSG, dm_arg); + printf("ioctl(-1, DM_TARGET_MSG, " + "{version=4.1.2, data_size=%zu, data_start=%u, " + "dev=makedev(18, 52), name=\"nnn\", uuid=\"uuu\", flags=0, " +# if VERBOSE + "/* misplaced struct dm_target_msg */" +# else /* !VERBOSE */ + "..." +# endif /* VERBOSE */ + "}) = -1 EBADF (%m)\n", + sizeof(*dm_arg), 0xffffffff); + + /* Inaccessible pointer */ + init_s(dm_arg, min_sizeof_dm_ioctl, 0); + dm_arg->data_size = sizeof(*dm_arg) + sizeof(struct dm_target_msg); + dm_arg->data_start = sizeof(*dm_arg); + ioctl(-1, DM_TARGET_MSG, dm_arg); + printf("ioctl(-1, DM_TARGET_MSG, " + "{version=4.1.2, data_size=%zu, data_start=%zu, " + "dev=makedev(18, 52), name=\"nnn\", uuid=\"uuu\", flags=0, " +# if VERBOSE + "%p" +# else /* !VERBOSE */ + "..." +# endif /* VERBOSE */ + "}) = -1 EBADF (%m)\n", + sizeof(*dm_arg) + sizeof(struct dm_target_msg), + sizeof(*dm_arg) +# if VERBOSE + , (char *) dm_arg + sizeof(*dm_arg) +# endif /* VERBOSE */ + ); + + /* Inaccessible string */ + init_s(&dm_arg_msg->ioc, sizeof(*dm_arg_msg), + offsetof(struct dm_target_msg_test, msg)); + dm_arg_msg->ioc.data_size = sizeof(*dm_arg_msg) + 1; + dm_arg_msg->msg.sector = (__u64) 0xdeadbeeffacef157ULL; + rc = ioctl(-1, DM_TARGET_MSG, dm_arg_msg); + errstr = sprintrc(rc); + printf("ioctl(-1, DM_TARGET_MSG, " + "{version=4.1.2, data_size=%zu, data_start=%zu, " + "dev=makedev(18, 52), name=\"nnn\", uuid=\"uuu\", flags=0, ", + sizeof(*dm_arg_msg) + 1, + offsetof(struct dm_target_msg_test, msg)); +# if VERBOSE + printf("{sector=%" PRI__u64 ", message=%p}", + (__u64) 0xdeadbeeffacef157ULL, + (char *) dm_arg_msg + + offsetof(struct dm_target_msg_test, msg.message)); +# else /* !VERBOSE */ + printf("..."); +# endif /* VERBOSE */ + printf("}) = %s\n", errstr); + + /* Zero-sied string */ + init_s(&dm_arg_msg->ioc, sizeof(*dm_arg_msg), + offsetof(struct dm_target_msg_test, msg)); + dm_arg_msg->msg.sector = (__u64) 0xdeadbeeffacef157ULL; + rc = ioctl(-1, DM_TARGET_MSG, dm_arg_msg); + errstr = sprintrc(rc); + printf("ioctl(-1, DM_TARGET_MSG, " + "{version=4.1.2, data_size=%zu, data_start=%zu, " + "dev=makedev(18, 52), name=\"nnn\", uuid=\"uuu\", flags=0, ", + sizeof(*dm_arg_msg), offsetof(struct dm_target_msg_test, msg)); +# if VERBOSE + printf("{sector=%" PRI__u64 ", message=\"\"}", + (__u64) 0xdeadbeeffacef157ULL); +# else /* !VERBOSE */ + printf("..."); +# endif /* VERBOSE */ + printf("}) = %s\n", errstr); + + + /* DM_DEV_SET_GEOMETRY */ + init_s(&s.ioc, sizeof(s), offsetof(struct s, u)); + strcpy(s.u.string, "10 20 30 40"); + ioctl(-1, DM_DEV_SET_GEOMETRY, &s); + printf("ioctl(-1, DM_DEV_SET_GEOMETRY, " + "{version=4.1.2, data_size=%u, data_start=%u, " + "dev=makedev(18, 52), name=\"nnn\", uuid=\"uuu\", flags=0, " +# if VERBOSE + "string=\"10 20 30 \"..." +# else /* !VERBOSE */ + "..." +# endif /* VERBOSE */ + "}) = -1 EBADF (%m)\n", + s.ioc.data_size, s.ioc.data_start); + + + /* DM_DEV_RENAME */ + /* Inaccessible data */ + init_s(dm_arg, min_sizeof_dm_ioctl, min_sizeof_dm_ioctl); + dm_arg->data_size = sizeof(*dm_arg); + memcpy(unaligned_dm_arg, dm_arg, offsetof(struct dm_ioctl, data)); + ioctl(-1, DM_DEV_RENAME, unaligned_dm_arg); + printf("ioctl(-1, DM_DEV_RENAME, " + "{version=4.1.2, data_size=%zu, data_start=%zu, " + "dev=makedev(18, 52), name=\"nnn\", uuid=\"uuu\", event_nr=0, " + "flags=0, " +# if VERBOSE + "string=%p" +# else /* !VERBOSE */ + "..." +# endif /* VERBOSE */ + "}) = -1 EBADF (%m)\n", + sizeof(*unaligned_dm_arg), min_sizeof_dm_ioctl +# if VERBOSE + , (char *) unaligned_dm_arg + min_sizeof_dm_ioctl +# endif /* VERBOSE */ + ); + + /* Incorrect data_start data */ + init_s(&s.ioc, sizeof(s), offsetof(struct s, u)); + s.ioc.data_start = 0xdeadbeef; + ioctl(-1, DM_DEV_RENAME, &s); + printf("ioctl(-1, DM_DEV_RENAME, " + "{version=4.1.2, data_size=%u, data_start=3735928559, " + "dev=makedev(18, 52), name=\"nnn\", uuid=\"uuu\", event_nr=0, " + "flags=0, " +# if VERBOSE + "/* misplaced string */" +# else /* !VERBOSE */ + "..." +# endif /* VERBOSE */ + "}) = -1 EBADF (%m)\n", + s.ioc.data_size); + + /* Strange but still valid data_start */ + init_s(&s.ioc, sizeof(s), offsetof(struct s, u)); + /* Curiously, this is a valid structure */ + s.ioc.data_start = offsetof(struct dm_ioctl, name) + 1; + ioctl(-1, DM_DEV_RENAME, &s); + printf("ioctl(-1, DM_DEV_RENAME, " + "{version=4.1.2, data_size=%u, data_start=%zu, " + "dev=makedev(18, 52), name=\"nnn\", uuid=\"uuu\", event_nr=0, " + "flags=0, " +# if VERBOSE + "string=\"nn\"" +# else /* !VERBOSE */ + "..." +# endif /* VERBOSE */ + "}) = -1 EBADF (%m)\n", + s.ioc.data_size, + offsetof(struct dm_ioctl, name) + 1); + + /* Correct data */ + init_s(&s.ioc, sizeof(s), offsetof(struct s, u)); + strcpy(s.u.string, "new long name"); + ioctl(-1, DM_DEV_RENAME, &s); + printf("ioctl(-1, DM_DEV_RENAME, " + "{version=4.1.2, data_size=%u, data_start=%u, " + "dev=makedev(18, 52), name=\"nnn\", uuid=\"uuu\", event_nr=0, " + "flags=0, " +# if VERBOSE + "string=\"new long \"..." +# else /* !VERBOSE */ + "..." +# endif /* VERBOSE */ + "}) = -1 EBADF (%m)\n", + s.ioc.data_size, s.ioc.data_start); + + + /* DM_TABLE_LOAD */ + init_s(&s.ioc, sizeof(s), offsetof(struct s, u)); + s.ioc.target_count = -1U; + ioctl(-1, DM_TABLE_LOAD, &s); + printf("ioctl(-1, DM_TABLE_LOAD, " + "{version=4.1.2, data_size=%u, data_start=%u, " + "dev=makedev(18, 52), name=\"nnn\", uuid=\"uuu\", " + "target_count=4294967295, flags=0, " +# if VERBOSE + "{sector_start=0, length=0, target_type=\"\", string=\"\"}, " + "/* misplaced struct dm_target_spec */ " +# endif /* VERBOSE */ + "...}) = -1 EBADF (%m)\n", + s.ioc.data_size, s.ioc.data_start); + + puts("+++ exited with 0 +++"); + return 0; +} + +#else /* !HAVE_LINUX_DM_IOCTL_H */ + +SKIP_MAIN_UNDEFINED("HAVE_LINUX_DM_IOCTL_H") + +#endif /* HAVE_LINUX_DM_IOCTL_H */ diff --git a/tests/ioctl_dm.test b/tests/ioctl_dm.test new file mode 100755 index 00000000..64852051 --- /dev/null +++ b/tests/ioctl_dm.test @@ -0,0 +1,12 @@ +#!/bin/sh + +# Check decoding of DM* ioctls. + +. "${srcdir=.}/init.sh" + +run_prog > /dev/null +run_strace -a16 -s9 -eioctl $args > "$EXP" +check_prog grep +grep -v '^ioctl([012],' < "$LOG" > "$OUT" +match_diff "$OUT" "$EXP" +rm -f "$EXP" "$OUT" diff --git a/xlat/dm_flags.in b/xlat/dm_flags.in new file mode 100644 index 00000000..fa734c81 --- /dev/null +++ b/xlat/dm_flags.in @@ -0,0 +1,19 @@ +DM_READONLY_FLAG (1 << 0) +DM_SUSPEND_FLAG (1 << 1) +/* Defined in lvm2/libdm/ioctl/libdm-iface.c */ +DM_EXISTS_FLAG (1 << 2) +DM_PERSISTENT_DEV_FLAG (1 << 3) +DM_STATUS_TABLE_FLAG (1 << 4) +DM_ACTIVE_PRESENT_FLAG (1 << 5) +DM_INACTIVE_PRESENT_FLAG (1 << 6) +DM_BUFFER_FULL_FLAG (1 << 8) +DM_SKIP_BDGET_FLAG (1 << 9) +DM_SKIP_LOCKFS_FLAG (1 << 10) +DM_NOFLUSH_FLAG (1 << 11) +DM_QUERY_INACTIVE_TABLE_FLAG (1 << 12) +DM_UEVENT_GENERATED_FLAG (1 << 13) +DM_UUID_FLAG (1 << 14) +DM_SECURE_DATA_FLAG (1 << 15) +DM_DATA_OUT_FLAG (1 << 16) +DM_DEFERRED_REMOVE (1 << 17) +DM_INTERNAL_SUSPEND_FLAG (1 << 18) -- 2.50.1