]> granicus.if.org Git - strace/blob - dm.c
Update SOL_* constants
[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 "print_fields.h"
39 # include <linux/dm-ioctl.h>
40 # include <linux/ioctl.h>
41
42 # if DM_VERSION_MAJOR == 4
43
44 /* Definitions for command which have been added later */
45
46 #  ifndef DM_LIST_VERSIONS
47 #   define DM_LIST_VERSIONS    _IOWR(DM_IOCTL, 0xd, struct dm_ioctl)
48 #  endif
49 #  ifndef DM_TARGET_MSG
50 #   define DM_TARGET_MSG       _IOWR(DM_IOCTL, 0xe, struct dm_ioctl)
51 #  endif
52 #  ifndef DM_DEV_SET_GEOMETRY
53 #   define DM_DEV_SET_GEOMETRY _IOWR(DM_IOCTL, 0xf, struct dm_ioctl)
54 #  endif
55
56
57 static void
58 dm_decode_device(const unsigned int code, const struct dm_ioctl *ioc)
59 {
60         switch (code) {
61         case DM_REMOVE_ALL:
62         case DM_LIST_DEVICES:
63         case DM_LIST_VERSIONS:
64                 break;
65         default:
66                 if (ioc->dev)
67                         PRINT_FIELD_DEV(", ", *ioc, dev);
68
69                 if (ioc->name[0])
70                         PRINT_FIELD_CSTRING(", ", *ioc, name);
71
72                 if (ioc->uuid[0])
73                         PRINT_FIELD_CSTRING(", ", *ioc, uuid);
74
75                 break;
76         }
77 }
78
79 static void
80 dm_decode_values(struct tcb *tcp, const unsigned int code,
81                  const struct dm_ioctl *ioc)
82 {
83         if (entering(tcp)) {
84                 switch (code) {
85                 case DM_TABLE_LOAD:
86                         PRINT_FIELD_U(", ", *ioc, target_count);
87                         break;
88                 case DM_DEV_SUSPEND:
89                         if (ioc->flags & DM_SUSPEND_FLAG)
90                                 break;
91                         /* Fall through */
92                 case DM_DEV_RENAME:
93                 case DM_DEV_REMOVE:
94                 case DM_DEV_WAIT:
95                         PRINT_FIELD_U(", ", *ioc, event_nr);
96                         break;
97                 }
98         } else if (!syserror(tcp)) {
99                 switch (code) {
100                 case DM_DEV_CREATE:
101                 case DM_DEV_RENAME:
102                 case DM_DEV_SUSPEND:
103                 case DM_DEV_STATUS:
104                 case DM_DEV_WAIT:
105                 case DM_TABLE_LOAD:
106                 case DM_TABLE_CLEAR:
107                 case DM_TABLE_DEPS:
108                 case DM_TABLE_STATUS:
109                 case DM_TARGET_MSG:
110                         PRINT_FIELD_U(", ", *ioc, target_count);
111                         PRINT_FIELD_U(", ", *ioc, open_count);
112                         PRINT_FIELD_U(", ", *ioc, event_nr);
113                         break;
114                 }
115         }
116 }
117
118 #include "xlat/dm_flags.h"
119
120 static void
121 dm_decode_flags(const struct dm_ioctl *ioc)
122 {
123         PRINT_FIELD_FLAGS(", ", *ioc, flags, dm_flags, "DM_???");
124 }
125
126 static void
127 dm_decode_dm_target_spec(struct tcb *const tcp, const kernel_ulong_t addr,
128                          const struct dm_ioctl *const ioc)
129 {
130         static const uint32_t target_spec_size =
131                 sizeof(struct dm_target_spec);
132         uint32_t i;
133         uint32_t offset = ioc->data_start;
134         uint32_t offset_end = 0;
135
136         if (abbrev(tcp)) {
137                 if (ioc->target_count)
138                         tprints(", ...");
139
140                 return;
141         }
142
143         for (i = 0; i < ioc->target_count; i++) {
144                 tprints(", ");
145
146                 if (i && offset <= offset_end)
147                         goto misplaced;
148
149                 offset_end = offset + target_spec_size;
150
151                 if (offset_end <= offset || offset_end > ioc->data_size)
152                         goto misplaced;
153
154                 if (i >= max_strlen) {
155                         tprints("...");
156                         break;
157                 }
158
159                 struct dm_target_spec s;
160
161                 if (umove_or_printaddr(tcp, addr + offset, &s))
162                         break;
163
164                 PRINT_FIELD_U("{", s, sector_start);
165                 PRINT_FIELD_U(", ", s, length);
166
167                 if (exiting(tcp))
168                         PRINT_FIELD_D(", ", s, status);
169
170                 PRINT_FIELD_CSTRING(", ", s, target_type);
171
172                 tprints(", string=");
173                 printstr_ex(tcp, addr + offset_end, ioc->data_size - offset_end,
174                              QUOTE_0_TERMINATED);
175                 tprints("}");
176
177                 if (entering(tcp))
178                         offset += s.next;
179                 else
180                         offset = ioc->data_start + s.next;
181         }
182
183         return;
184
185 misplaced:
186         tprints("???");
187         tprints_comment("misplaced struct dm_target_spec");
188 }
189
190 bool
191 dm_print_dev(struct tcb *tcp, void *dev_ptr, size_t dev_size, void *dummy)
192 {
193         uint64_t *dev = (uint64_t *) dev_ptr;
194
195         print_dev_t(*dev);
196
197         return 1;
198 }
199
200 static void
201 dm_decode_dm_target_deps(struct tcb *const tcp, const kernel_ulong_t addr,
202                          const struct dm_ioctl *const ioc)
203 {
204         if (ioc->data_start == ioc->data_size)
205                 return;
206
207         tprints(", ");
208
209         if (abbrev(tcp)) {
210                 tprints("...");
211                 return;
212         }
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 (offset_end <= offset || offset_end > ioc->data_size)
223                 goto misplaced;
224
225         if (umove_or_printaddr(tcp, addr + offset, &s))
226                 return;
227
228         space = (ioc->data_size - offset_end) / sizeof(dev_buf);
229
230         if (s.count > space)
231                 goto misplaced;
232
233         PRINT_FIELD_U("{", s, count);
234
235         tprints(", deps=");
236         print_array(tcp, addr + offset_end, s.count, &dev_buf, sizeof(dev_buf),
237                     umoven_or_printaddr, dm_print_dev, NULL);
238
239         tprints("}");
240
241         return;
242
243 misplaced:
244         tprints("???");
245         tprints_comment("misplaced struct dm_target_deps");
246 }
247
248 static void
249 dm_decode_dm_name_list(struct tcb *const tcp, const kernel_ulong_t addr,
250                        const struct dm_ioctl *const ioc)
251 {
252         static const uint32_t name_list_name_offs =
253                 offsetof(struct dm_name_list, name);
254         struct dm_name_list s;
255         uint32_t offset = ioc->data_start;
256         uint32_t offset_end = 0;
257         uint32_t count;
258
259         if (ioc->data_start == ioc->data_size)
260                 return;
261
262         if (abbrev(tcp)) {
263                 tprints(", ...");
264                 return;
265         }
266
267         for (count = 0;; count++) {
268                 tprints(", ");
269
270                 if (count && offset <= offset_end)
271                         goto misplaced;
272
273                 offset_end = offset + name_list_name_offs;
274
275                 if (offset_end <= offset || offset_end > ioc->data_size)
276                         goto misplaced;
277
278                 if (count >= max_strlen) {
279                         tprints("...");
280                         break;
281                 }
282
283                 if (umove_or_printaddr(tcp, addr + offset, &s))
284                         break;
285
286                 PRINT_FIELD_DEV("{", s, dev);
287                 tprints(", name=");
288                 printstr_ex(tcp, addr + offset_end, ioc->data_size - offset_end,
289                             QUOTE_0_TERMINATED);
290                 tprints("}");
291
292                 if (!s.next)
293                         break;
294
295                 offset += s.next;
296         }
297
298         return;
299
300 misplaced:
301         tprints("???");
302         tprints_comment("misplaced struct dm_name_list");
303 }
304
305 static void
306 dm_decode_dm_target_versions(struct tcb *const tcp, const kernel_ulong_t addr,
307                              const struct dm_ioctl *const ioc)
308 {
309         static const uint32_t target_vers_name_offs =
310                 offsetof(struct dm_target_versions, name);
311         struct dm_target_versions s;
312         uint32_t offset = ioc->data_start;
313         uint32_t offset_end = 0;
314         uint32_t count;
315
316         if (ioc->data_start == ioc->data_size)
317                 return;
318
319         if (abbrev(tcp)) {
320                 tprints(", ...");
321                 return;
322         }
323
324         for (count = 0;; count++) {
325                 tprints(", ");
326
327                 if (count && offset <= offset_end)
328                         goto misplaced;
329
330                 offset_end = offset + target_vers_name_offs;
331
332                 if (offset_end <= offset || offset_end > ioc->data_size)
333                         goto misplaced;
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         }
354
355         return;
356
357 misplaced:
358         tprints("???");
359         tprints_comment("misplaced struct dm_target_versions");
360 }
361
362 static void
363 dm_decode_dm_target_msg(struct tcb *const tcp, const kernel_ulong_t addr,
364                         const struct dm_ioctl *const ioc)
365 {
366         if (ioc->data_start == ioc->data_size)
367                 return;
368
369         tprints(", ");
370
371         if (abbrev(tcp)) {
372                 tprints("...");
373                 return;
374         }
375
376         static const uint32_t target_msg_message_offs =
377                 offsetof(struct dm_target_msg, message);
378         uint32_t offset = ioc->data_start;
379         uint32_t offset_end = offset + target_msg_message_offs;
380
381         if (offset_end > offset && offset_end <= ioc->data_size) {
382                 struct dm_target_msg s;
383
384                 if (umove_or_printaddr(tcp, addr + offset, &s))
385                         return;
386
387                 PRINT_FIELD_U("{", s, sector);
388                 tprints(", message=");
389                 printstr_ex(tcp, addr + offset_end, ioc->data_size - offset_end,
390                             QUOTE_0_TERMINATED);
391                 tprints("}");
392         } else {
393                 tprints("???");
394                 tprints_comment("misplaced struct dm_target_msg");
395         }
396 }
397
398 static void
399 dm_decode_string(struct tcb *const tcp, const kernel_ulong_t addr,
400                  const struct dm_ioctl *const ioc)
401 {
402         tprints(", ");
403
404         if (abbrev(tcp)) {
405                 tprints("...");
406                 return;
407         }
408
409         uint32_t offset = ioc->data_start;
410
411         if (offset <= ioc->data_size) {
412                 tprints("string=");
413                 printstr_ex(tcp, addr + offset, ioc->data_size - offset,
414                             QUOTE_0_TERMINATED);
415         } else {
416                 tprints("???");
417                 tprints_comment("misplaced string");
418         }
419 }
420
421 static inline bool
422 dm_ioctl_has_params(const unsigned int code)
423 {
424         switch (code) {
425         case DM_VERSION:
426         case DM_REMOVE_ALL:
427         case DM_DEV_CREATE:
428         case DM_DEV_REMOVE:
429         case DM_DEV_SUSPEND:
430         case DM_DEV_STATUS:
431         case DM_TABLE_CLEAR:
432                 return false;
433         }
434
435         return true;
436 }
437
438 static int
439 dm_known_ioctl(struct tcb *const tcp, const unsigned int code,
440                const kernel_ulong_t arg)
441 {
442         struct dm_ioctl *ioc = NULL;
443         struct dm_ioctl *entering_ioc = NULL;
444         bool ioc_changed = false;
445
446         if (entering(tcp)) {
447                 ioc = malloc(sizeof(*ioc));
448                 if (!ioc)
449                         return 0;
450         } else {
451                 ioc = alloca(sizeof(*ioc));
452         }
453
454         if ((umoven(tcp, arg, offsetof(struct dm_ioctl, data), ioc) < 0) ||
455             (ioc->data_size < offsetof(struct dm_ioctl, data_size))) {
456                 if (entering(tcp))
457                         free(ioc);
458                 return 0;
459         }
460         if (entering(tcp))
461                 set_tcb_priv_data(tcp, ioc, free);
462         else {
463                 entering_ioc = get_tcb_priv_data(tcp);
464
465                 /*
466                  * retrieve_status, __dev_status called only in case of success,
467                  * so it looks like there's no need to check open_count,
468                  * event_nr, target_count, dev fields for change (they are
469                  * printed only in case of absence of errors).
470                  */
471                 if (!entering_ioc ||
472                     (ioc->version[0] != entering_ioc->version[0]) ||
473                     (ioc->version[1] != entering_ioc->version[1]) ||
474                     (ioc->version[2] != entering_ioc->version[2]) ||
475                     (ioc->data_size != entering_ioc->data_size) ||
476                     (ioc->data_start != entering_ioc->data_start) ||
477                     (ioc->flags != entering_ioc->flags))
478                         ioc_changed = true;
479         }
480
481         if (exiting(tcp) && syserror(tcp) && !ioc_changed)
482                 return RVAL_IOCTL_DECODED;
483
484         /*
485          * device mapper code uses %d in some places and %u in another, but
486          * fields themselves are declared as __u32.
487          */
488         tprintf("%s{version=%u.%u.%u",  entering(tcp) ? ", " : " => ",
489                 ioc->version[0], ioc->version[1], ioc->version[2]);
490         /*
491          * if we use a different version of ABI, do not attempt to decode
492          * ioctl fields
493          */
494         if (ioc->version[0] != DM_VERSION_MAJOR) {
495                 tprints_comment("unsupported device mapper ABI version");
496                 goto skip;
497         }
498
499         PRINT_FIELD_U(", ", *ioc, data_size);
500
501         if (ioc->data_size < offsetof(struct dm_ioctl, data)) {
502                 tprints_comment("data_size too small");
503                 goto skip;
504         }
505
506         if (dm_ioctl_has_params(code))
507                 PRINT_FIELD_U(", ", *ioc, data_start);
508
509         dm_decode_device(code, ioc);
510         dm_decode_values(tcp, code, ioc);
511         dm_decode_flags(ioc);
512
513         switch (code) {
514         case DM_DEV_WAIT:
515         case DM_TABLE_STATUS:
516                 if (entering(tcp) || syserror(tcp))
517                         break;
518                 dm_decode_dm_target_spec(tcp, arg, ioc);
519                 break;
520         case DM_TABLE_LOAD:
521                 if (exiting(tcp))
522                         break;
523                 dm_decode_dm_target_spec(tcp, arg, ioc);
524                 break;
525         case DM_TABLE_DEPS:
526                 if (entering(tcp) || syserror(tcp))
527                         break;
528                 dm_decode_dm_target_deps(tcp, arg, ioc);
529                 break;
530         case DM_LIST_DEVICES:
531                 if (entering(tcp) || syserror(tcp))
532                         break;
533                 dm_decode_dm_name_list(tcp, arg, ioc);
534                 break;
535         case DM_LIST_VERSIONS:
536                 if (entering(tcp) || syserror(tcp))
537                         break;
538                 dm_decode_dm_target_versions(tcp, arg, ioc);
539                 break;
540         case DM_TARGET_MSG:
541                 if (entering(tcp))
542                         dm_decode_dm_target_msg(tcp, arg, ioc);
543                 else if (!syserror(tcp) && ioc->flags & DM_DATA_OUT_FLAG)
544                         dm_decode_string(tcp, arg, ioc);
545                 break;
546         case DM_DEV_RENAME:
547         case DM_DEV_SET_GEOMETRY:
548                 if (exiting(tcp))
549                         break;
550                 dm_decode_string(tcp, arg, ioc);
551                 break;
552         }
553
554  skip:
555         tprints("}");
556         return entering(tcp) ? 0 : RVAL_IOCTL_DECODED;
557 }
558
559 int
560 dm_ioctl(struct tcb *const tcp, const unsigned int code, const kernel_ulong_t arg)
561 {
562         switch (code) {
563         case DM_VERSION:
564         case DM_REMOVE_ALL:
565         case DM_LIST_DEVICES:
566         case DM_DEV_CREATE:
567         case DM_DEV_REMOVE:
568         case DM_DEV_RENAME:
569         case DM_DEV_SUSPEND:
570         case DM_DEV_STATUS:
571         case DM_DEV_WAIT:
572         case DM_TABLE_LOAD:
573         case DM_TABLE_CLEAR:
574         case DM_TABLE_DEPS:
575         case DM_TABLE_STATUS:
576         case DM_LIST_VERSIONS:
577         case DM_TARGET_MSG:
578         case DM_DEV_SET_GEOMETRY:
579                 return dm_known_ioctl(tcp, code, arg);
580         default:
581                 return RVAL_DECODED;
582         }
583 }
584
585 # else /* !(DM_VERSION_MAJOR == 4) */
586
587 int
588 dm_ioctl(struct tcb *const tcp, const unsigned int code, const kernel_ulong_t arg)
589 {
590         return RVAL_DECODED;
591 }
592
593 # endif /* DM_VERSION_MAJOR == 4 */
594 #endif /* HAVE_LINUX_DM_IOCTL_H */