]> granicus.if.org Git - strace/blob - dm.c
tests: tabulate pread64-pwrite64.test
[strace] / dm.c
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
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
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
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
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                 tprints("}");
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
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
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
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
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
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
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
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
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
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
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 */