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