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