xref: /dragonfly/sys/dev/disk/dm/dm_ioctl.c (revision 8477f730259ff96c34fdc0168832038bf1bff338)
1 /* $NetBSD: dm_ioctl.c,v 1.21 2010/02/25 20:48:58 jakllsch Exp $      */
2 
3 /*
4  * Copyright (c) 2010-2011 Alex Hornung <alex@alexhornung.com>
5  * Copyright (c) 2008 The NetBSD Foundation, Inc.
6  * All rights reserved.
7  *
8  * This code is derived from software contributed to The NetBSD Foundation
9  * by Adam Hamsik.
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  *
20  * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
21  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
22  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
23  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
24  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
25  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
26  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
27  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
28  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
29  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
30  * POSSIBILITY OF SUCH DAMAGE.
31  */
32 
33 /*
34  * Locking is used to synchronise between ioctl calls and between dm_table's
35  * users.
36  *
37  * ioctl locking:
38  * Simple reference counting, to count users of device will be used routines
39  * dm_dev_busy/dm_dev_unbusy are used for that.
40  * dm_dev_lookup/dm_dev_rem call dm_dev_busy before return(caller is therefore
41  * holder of reference_counter last).
42  *
43  * ioctl routines which change/remove dm_dev parameters must wait on
44  * dm_dev::dev_cv and when last user will call dm_dev_unbusy they will wake
45  * up them.
46  *
47  * table_head locking:
48  * To access table entries dm_table_* routines must be used.
49  *
50  * dm_table_get_entry will increment table users reference
51  * counter. It will return active or inactive table depends
52  * on uint8_t argument.
53  *
54  * dm_table_release must be called for every table_entry from
55  * dm_table_get_entry. Between these to calls tables can'tbe switched
56  * or destroyed.
57  *
58  * dm_table_head_init initialize talbe_entries TAILQS and io_cv.
59  *
60  * dm_table_head_destroy destroy cv.
61  *
62  * There are two types of users for dm_table_head first type will
63  * only read list and try to do anything with it e.g. dmstrategy,
64  * dm_table_size etc. There is another user for table_head which wants
65  * to change table lists e.g. dm_dev_resume_ioctl, dm_dev_remove_ioctl,
66  * dm_table_clear_ioctl.
67  *
68  * NOTE: It is not allowed to call dm_table_destroy, dm_table_switch_tables
69  *       with hold table reference counter. Table reference counter is hold
70  *       after calling dm_table_get_entry routine. After calling this
71  *       function user must call dm_table_release before any writer table
72  *       operation.
73  *
74  * Example: dm_table_get_entry
75  *          dm_table_destroy/dm_table_switch_tables
76  * This exaple will lead to deadlock situation because after dm_table_get_entry
77  * table reference counter is != 0 and dm_table_destroy have to wait on cv until
78  * reference counter is 0.
79  *
80  */
81 
82 #include <sys/param.h>
83 #include <sys/device.h>
84 #include <sys/malloc.h>
85 #include <cpu/atomic.h>
86 #include <dev/disk/dm/dm.h>
87 #include <dev/disk/dm/netbsd-dm.h>
88 
89 static int
90 dm_table_deps(dm_table_entry_t *, prop_array_t);
91 static int
92 dm_table_init(dm_target_t *, dm_table_entry_t *, char *);
93 static int
94 dm_table_status(dm_table_entry_t *, prop_dictionary_t, uint32_t);
95 
96 static __inline
dm_add_flag(prop_dictionary_t dp,uint32_t * fp,const uint32_t val)97 void dm_add_flag(prop_dictionary_t dp, uint32_t *fp, const uint32_t val)
98 {
99           KKASSERT(dp != NULL);
100           prop_dictionary_get_uint32(dp, DM_IOCTL_FLAGS, fp);
101           (*fp) |= val;
102           prop_dictionary_set_uint32(dp, DM_IOCTL_FLAGS, *fp);
103           KKASSERT((*fp) & val);
104 }
105 
106 static __inline
dm_remove_flag(prop_dictionary_t dp,uint32_t * fp,const uint32_t val)107 void dm_remove_flag(prop_dictionary_t dp, uint32_t *fp, const uint32_t val)
108 {
109           KKASSERT(dp != NULL);
110           prop_dictionary_get_uint32(dp, DM_IOCTL_FLAGS, fp);
111           (*fp) &= ~val;
112           prop_dictionary_set_uint32(dp, DM_IOCTL_FLAGS, *fp);
113           KKASSERT(!((*fp) & val));
114 }
115 
116 /*
117  * Print flags sent to the kernel from libdevmapper.
118  */
119 static int
dm_dbg_print_flags(uint32_t flags)120 dm_dbg_print_flags(uint32_t flags)
121 {
122           if (dm_debug_level == 0)
123                     return 1;
124 
125           kprintf("%s --- flags=%08X\n", __func__, flags);
126 
127           if (flags & DM_READONLY_FLAG)
128                     kprintf("  DM_READONLY_FLAG\n");
129           if (flags & DM_SUSPEND_FLAG)
130                     kprintf("  DM_SUSPEND_FLAG\n");
131           if (flags & DM_EXISTS_FLAG)
132                     kprintf("  DM_EXISTS_FLAG\n");
133           if (flags & DM_PERSISTENT_DEV_FLAG)
134                     kprintf("  DM_PERSISTENT_DEV_FLAG\n");
135           if (flags & DM_STATUS_TABLE_FLAG)
136                     kprintf("  DM_STATUS_TABLE_FLAG\n");
137           if (flags & DM_ACTIVE_PRESENT_FLAG)
138                     kprintf("  DM_ACTIVE_PRESENT_FLAG\n");
139           if (flags & DM_INACTIVE_PRESENT_FLAG)
140                     kprintf("  DM_INACTIVE_PRESENT_FLAG\n");
141           if (flags & DM_BUFFER_FULL_FLAG)
142                     kprintf("  DM_BUFFER_FULL_FLAG\n");
143           if (flags & DM_SKIP_BDGET_FLAG)
144                     kprintf("  DM_SKIP_BDGET_FLAG\n");
145           if (flags & DM_SKIP_LOCKFS_FLAG)
146                     kprintf("  DM_SKIP_LOCKFS_FLAG\n");
147           if (flags & DM_NOFLUSH_FLAG)
148                     kprintf("  DM_NOFLUSH_FLAG\n");
149           if (flags & DM_QUERY_INACTIVE_TABLE_FLAG)
150                     kprintf("  DM_QUERY_INACTIVE_TABLE_FLAG\n");
151           if (flags & DM_UUID_FLAG)
152                     kprintf("  DM_UUID_FLAG\n");
153           if (flags & DM_SECURE_DATA_FLAG)
154                     kprintf("  DM_SECURE_DATA_FLAG\n");
155 
156           return 0;
157 }
158 
159 /*
160  * Get list of all available targets from global
161  * target list and sent them back to libdevmapper.
162  */
163 int
dm_list_versions_ioctl(prop_dictionary_t dm_dict)164 dm_list_versions_ioctl(prop_dictionary_t dm_dict)
165 {
166           prop_array_t target_list;
167           uint32_t flags;
168 
169           flags = 0;
170 
171           prop_dictionary_get_uint32(dm_dict, DM_IOCTL_FLAGS, &flags);
172 
173           dm_dbg_print_flags(flags);
174           target_list = dm_target_prop_list();
175 
176           prop_dictionary_set(dm_dict, DM_IOCTL_CMD_DATA, target_list);
177           prop_object_release(target_list);
178 
179           return 0;
180 }
181 
182 /*
183  * Create in-kernel entry for device. Device attributes such as name, uuid are
184  * taken from proplib dictionary.
185  */
186 int
dm_dev_create_ioctl(prop_dictionary_t dm_dict)187 dm_dev_create_ioctl(prop_dictionary_t dm_dict)
188 {
189           dm_dev_t *dmv;
190           const char *name, *uuid;
191           int r;
192           uint32_t flags;
193 
194           r = 0;
195           flags = 0;
196           name = NULL;
197           uuid = NULL;
198 
199           /* Get needed values from dictionary. */
200           prop_dictionary_get_cstring_nocopy(dm_dict, DM_IOCTL_NAME, &name);
201           prop_dictionary_get_cstring_nocopy(dm_dict, DM_IOCTL_UUID, &uuid);
202           prop_dictionary_get_uint32(dm_dict, DM_IOCTL_FLAGS, &flags);
203 
204           dm_dbg_print_flags(flags);
205 
206           /* Lookup name and uuid if device already exist quit. */
207           if ((dmv = dm_dev_lookup(name, uuid, -1)) != NULL) {
208                     dm_add_flag(dm_dict, &flags, DM_EXISTS_FLAG);     /* Device already exists */
209                     dm_dev_unbusy(dmv);
210                     return EEXIST;
211           }
212 
213           r = dm_dev_create(&dmv, name, uuid, flags);
214           if (r == 0) {
215                     prop_dictionary_set_uint32(dm_dict, DM_IOCTL_MINOR, dmv->minor);
216                     dm_add_flag(dm_dict, &flags, DM_EXISTS_FLAG);
217                     dm_remove_flag(dm_dict, &flags, DM_INACTIVE_PRESENT_FLAG);
218           }
219 
220           return r;
221 }
222 
223 /*
224  * Get list of created device-mapper devices from global list and
225  * send it to kernel.
226  *
227  * Output dictionary:
228  *
229  * <key>cmd_data</key>
230  *  <array>
231  *   <dict>
232  *    <key>name<key>
233  *    <string>...</string>
234  *
235  *    <key>dev</key>
236  *    <integer>...</integer>
237  *   </dict>
238  *  </array>
239  */
240 int
dm_dev_list_ioctl(prop_dictionary_t dm_dict)241 dm_dev_list_ioctl(prop_dictionary_t dm_dict)
242 {
243           prop_array_t dev_list;
244 
245           uint32_t flags;
246 
247           flags = 0;
248 
249           prop_dictionary_get_uint32(dm_dict, DM_IOCTL_FLAGS, &flags);
250 
251           dm_dbg_print_flags(flags);
252 
253           dev_list = dm_dev_prop_list();
254 
255           prop_dictionary_set(dm_dict, DM_IOCTL_CMD_DATA, dev_list);
256           prop_object_release(dev_list);
257 
258           return 0;
259 }
260 
261 /*
262  * Rename selected devices old name is in struct dm_ioctl.
263  * new name is taken from dictionary.
264  *
265  * <key>cmd_data</key>
266  *  <array>
267  *   <string>...</string>
268  *  </array>
269  */
270 int
dm_dev_rename_ioctl(prop_dictionary_t dm_dict)271 dm_dev_rename_ioctl(prop_dictionary_t dm_dict)
272 {
273 #if 0
274           prop_array_t cmd_array;
275           dm_dev_t *dmv;
276 
277           const char *name, *uuid, *n_name;
278           uint32_t flags, minor;
279 
280           name = NULL;
281           uuid = NULL;
282           minor = 0;
283 
284           /* Get needed values from dictionary. */
285           prop_dictionary_get_cstring_nocopy(dm_dict, DM_IOCTL_NAME, &name);
286           prop_dictionary_get_cstring_nocopy(dm_dict, DM_IOCTL_UUID, &uuid);
287           prop_dictionary_get_uint32(dm_dict, DM_IOCTL_FLAGS, &flags);
288           prop_dictionary_get_uint32(dm_dict, DM_IOCTL_MINOR, &minor);
289 
290           dm_dbg_print_flags(flags);
291 
292           cmd_array = prop_dictionary_get(dm_dict, DM_IOCTL_CMD_DATA);
293 
294           prop_array_get_cstring_nocopy(cmd_array, 0, &n_name);
295 
296           if (strlen(n_name) + 1 > DM_NAME_LEN)
297                     return EINVAL;
298 
299           if ((dmv = dm_dev_lookup_evict(name, uuid, minor)) == NULL) {
300                     dm_remove_flag(dm_dict, &flags, DM_EXISTS_FLAG);
301                     return ENOENT;
302           }
303           /* change device name */
304           /*
305            * XXX How to deal with this change, name only used in
306            * dm_dev_routines, should I add dm_dev_change_name which will run
307            * under the dm_dev_list mutex ?
308            */
309           strlcpy(dmv->name, n_name, DM_NAME_LEN);
310 
311           prop_dictionary_set_uint32(dm_dict, DM_IOCTL_OPEN, dmv->table_head.io_cnt);
312           prop_dictionary_set_uint32(dm_dict, DM_IOCTL_MINOR, dmv->minor);
313           prop_dictionary_set_cstring(dm_dict, DM_IOCTL_UUID, dmv->uuid);
314 
315           dm_dev_insert(dmv);
316 #endif
317 
318           /*
319            * XXX: the rename is not yet implemented. The main complication
320            *        here is devfs. We'd probably need a new function, rename_dev()
321            *        that would trigger a node rename in devfs.
322            */
323           kprintf("dm_dev_rename_ioctl called, but not implemented!\n");
324           return ENOSYS;
325 }
326 
327 /*
328  * Remove device
329  */
330 int
dm_dev_remove_ioctl(prop_dictionary_t dm_dict)331 dm_dev_remove_ioctl(prop_dictionary_t dm_dict)
332 {
333           dm_dev_t *dmv;
334           const char *name, *uuid;
335           uint32_t flags, minor, is_open;
336 
337           flags = 0;
338           name = NULL;
339           uuid = NULL;
340 
341           /* Get needed values from dictionary. */
342           prop_dictionary_get_cstring_nocopy(dm_dict, DM_IOCTL_NAME, &name);
343           prop_dictionary_get_cstring_nocopy(dm_dict, DM_IOCTL_UUID, &uuid);
344           prop_dictionary_get_uint32(dm_dict, DM_IOCTL_FLAGS, &flags);
345           prop_dictionary_get_uint32(dm_dict, DM_IOCTL_MINOR, &minor);
346 
347           dm_dbg_print_flags(flags);
348 
349           if ((dmv = dm_dev_lookup(name, uuid, minor)) == NULL) {
350                     dm_remove_flag(dm_dict, &flags, DM_EXISTS_FLAG);
351                     return ENOENT;
352           }
353 
354           is_open = dmv->is_open;
355 
356           dm_dev_unbusy(dmv);
357 
358           if (is_open)
359                     return EBUSY;
360 
361           return dm_dev_remove(dmv);
362 }
363 
364 /*
365  * Try to remove all devices
366  */
367 int
dm_dev_remove_all_ioctl(prop_dictionary_t dm_dict)368 dm_dev_remove_all_ioctl(prop_dictionary_t dm_dict)
369 {
370           uint32_t flags = 0;
371 
372           /* Get needed values from dictionary. */
373           prop_dictionary_get_uint32(dm_dict, DM_IOCTL_FLAGS, &flags);
374 
375           dm_dbg_print_flags(flags);
376 
377           /* Gently remove all devices, if possible */
378           return dm_dev_remove_all(1);
379 }
380 
381 /*
382  * Return actual state of device to libdevmapper.
383  */
384 int
dm_dev_status_ioctl(prop_dictionary_t dm_dict)385 dm_dev_status_ioctl(prop_dictionary_t dm_dict)
386 {
387           dm_dev_t *dmv;
388           const char *name, *uuid;
389           uint32_t flags, j, minor;
390 
391           name = NULL;
392           uuid = NULL;
393           flags = 0;
394           j = 0;
395 
396           prop_dictionary_get_cstring_nocopy(dm_dict, DM_IOCTL_NAME, &name);
397           prop_dictionary_get_cstring_nocopy(dm_dict, DM_IOCTL_UUID, &uuid);
398           prop_dictionary_get_uint32(dm_dict, DM_IOCTL_FLAGS, &flags);
399           prop_dictionary_get_uint32(dm_dict, DM_IOCTL_MINOR, &minor);
400 
401           if ((dmv = dm_dev_lookup(name, uuid, minor)) == NULL) {
402                     dm_remove_flag(dm_dict, &flags, DM_EXISTS_FLAG);
403                     return ENOENT;
404           }
405           dm_dbg_print_flags(dmv->flags);
406 
407           prop_dictionary_set_uint32(dm_dict, DM_IOCTL_OPEN, dmv->table_head.io_cnt);
408           prop_dictionary_set_uint32(dm_dict, DM_IOCTL_MINOR, dmv->minor);
409           prop_dictionary_set_cstring(dm_dict, DM_IOCTL_UUID, dmv->uuid);
410 
411           if (dmv->flags & DM_SUSPEND_FLAG)
412                     dm_add_flag(dm_dict, &flags, DM_SUSPEND_FLAG);
413 
414           /*
415            * Add status flags for tables I have to check both active and
416            * inactive tables.
417            */
418           if ((j = dm_table_get_target_count(&dmv->table_head, DM_TABLE_ACTIVE)))
419                     dm_add_flag(dm_dict, &flags, DM_ACTIVE_PRESENT_FLAG);
420           else
421                     dm_remove_flag(dm_dict, &flags, DM_ACTIVE_PRESENT_FLAG);
422 
423           prop_dictionary_set_uint32(dm_dict, DM_IOCTL_TARGET_COUNT, j);
424 
425           if (dm_table_get_target_count(&dmv->table_head, DM_TABLE_INACTIVE))
426                     dm_add_flag(dm_dict, &flags, DM_INACTIVE_PRESENT_FLAG);
427           else
428                     dm_remove_flag(dm_dict, &flags, DM_INACTIVE_PRESENT_FLAG);
429 
430           dm_dev_unbusy(dmv);
431 
432           return 0;
433 }
434 
435 /*
436  * Set only flag to suggest that device is suspended. This call is
437  * not supported in NetBSD.
438  */
439 int
dm_dev_suspend_ioctl(prop_dictionary_t dm_dict)440 dm_dev_suspend_ioctl(prop_dictionary_t dm_dict)
441 {
442           dm_dev_t *dmv;
443           const char *name, *uuid;
444           uint32_t flags, minor;
445 
446           name = NULL;
447           uuid = NULL;
448           flags = 0;
449 
450           prop_dictionary_get_cstring_nocopy(dm_dict, DM_IOCTL_NAME, &name);
451           prop_dictionary_get_cstring_nocopy(dm_dict, DM_IOCTL_UUID, &uuid);
452           prop_dictionary_get_uint32(dm_dict, DM_IOCTL_FLAGS, &flags);
453           prop_dictionary_get_uint32(dm_dict, DM_IOCTL_MINOR, &minor);
454 
455           if ((dmv = dm_dev_lookup(name, uuid, minor)) == NULL) {
456                     dm_remove_flag(dm_dict, &flags, DM_EXISTS_FLAG);
457                     return ENOENT;
458           }
459           atomic_set_int(&dmv->flags, DM_SUSPEND_FLAG);
460 
461           dm_dbg_print_flags(dmv->flags);
462 
463           prop_dictionary_set_uint32(dm_dict, DM_IOCTL_OPEN, dmv->table_head.io_cnt);
464           prop_dictionary_set_uint32(dm_dict, DM_IOCTL_FLAGS, dmv->flags);
465           prop_dictionary_set_uint32(dm_dict, DM_IOCTL_MINOR, dmv->minor);
466 
467           dm_dev_unbusy(dmv);
468 
469           /* Add flags to dictionary flag after dmv -> dict copy */
470           dm_add_flag(dm_dict, &flags, DM_EXISTS_FLAG);
471 
472           return 0;
473 }
474 
475 /*
476  * Simulate Linux behaviour better and switch tables here and not in
477  * dm_table_load_ioctl.
478  */
479 int
dm_dev_resume_ioctl(prop_dictionary_t dm_dict)480 dm_dev_resume_ioctl(prop_dictionary_t dm_dict)
481 {
482           dm_dev_t *dmv;
483           const char *name, *uuid;
484           uint32_t flags, minor;
485 
486           name = NULL;
487           uuid = NULL;
488           flags = 0;
489 
490           /*
491            * char *xml; xml = prop_dictionary_externalize(dm_dict);
492            * kprintf("%s\n",xml);
493            */
494 
495           prop_dictionary_get_cstring_nocopy(dm_dict, DM_IOCTL_NAME, &name);
496           prop_dictionary_get_cstring_nocopy(dm_dict, DM_IOCTL_UUID, &uuid);
497           prop_dictionary_get_uint32(dm_dict, DM_IOCTL_FLAGS, &flags);
498           prop_dictionary_get_uint32(dm_dict, DM_IOCTL_MINOR, &minor);
499 
500           /* Remove device from global device list */
501           if ((dmv = dm_dev_lookup(name, uuid, minor)) == NULL) {
502                     dm_remove_flag(dm_dict, &flags, DM_EXISTS_FLAG);
503                     return ENOENT;
504           }
505           atomic_clear_int(&dmv->flags, (DM_SUSPEND_FLAG | DM_INACTIVE_PRESENT_FLAG));
506           atomic_set_int(&dmv->flags, DM_ACTIVE_PRESENT_FLAG);
507 
508           dm_table_switch_tables(&dmv->table_head);
509 
510           dm_add_flag(dm_dict, &flags, DM_EXISTS_FLAG);
511 
512           dmsetdiskinfo(dmv->diskp, &dmv->table_head);
513 
514           prop_dictionary_set_uint32(dm_dict, DM_IOCTL_OPEN, dmv->table_head.io_cnt);
515           prop_dictionary_set_uint32(dm_dict, DM_IOCTL_FLAGS, flags);
516           prop_dictionary_set_uint32(dm_dict, DM_IOCTL_MINOR, dmv->minor);
517 
518           dm_dev_unbusy(dmv);
519 
520           /* Destroy inactive table after resume. */
521           dm_table_destroy(&dmv->table_head, DM_TABLE_INACTIVE);
522 
523           return 0;
524 }
525 
526 /*
527  * Table management routines
528  * lvm2tools doens't send name/uuid to kernel with table
529  * for lookup I have to use minor number.
530  */
531 
532 /*
533  * Remove inactive table from device. Routines which work's with inactive tables
534  * doesn't need to synchronise with dmstrategy. They can synchronise themselves with mutex?.
535  */
536 int
dm_table_clear_ioctl(prop_dictionary_t dm_dict)537 dm_table_clear_ioctl(prop_dictionary_t dm_dict)
538 {
539           dm_dev_t *dmv;
540           const char *name, *uuid;
541           uint32_t flags, minor;
542 
543           dmv = NULL;
544           name = NULL;
545           uuid = NULL;
546           flags = 0;
547           minor = 0;
548 
549           prop_dictionary_get_cstring_nocopy(dm_dict, DM_IOCTL_NAME, &name);
550           prop_dictionary_get_cstring_nocopy(dm_dict, DM_IOCTL_UUID, &uuid);
551           prop_dictionary_get_uint32(dm_dict, DM_IOCTL_FLAGS, &flags);
552           prop_dictionary_get_uint32(dm_dict, DM_IOCTL_MINOR, &minor);
553 
554           dmdebug("Clearing inactive table from device: %s--%s\n",
555               name, uuid);
556 
557           if ((dmv = dm_dev_lookup(name, uuid, minor)) == NULL) {
558                     dm_remove_flag(dm_dict, &flags, DM_EXISTS_FLAG);
559                     return ENOENT;
560           }
561           /* Select unused table */
562           dm_table_destroy(&dmv->table_head, DM_TABLE_INACTIVE);
563 
564           atomic_clear_int(&dmv->flags, DM_INACTIVE_PRESENT_FLAG);
565 
566           dm_dev_unbusy(dmv);
567 
568           return 0;
569 }
570 
571 /*
572  * Get list of physical devices for active table.
573  * Get dev_t from pdev vnode and insert it into cmd_array.
574  *
575  * XXX. This function is called from lvm2tools to get information
576  *      about physical devices, too e.g. during vgcreate.
577  */
578 int
dm_table_deps_ioctl(prop_dictionary_t dm_dict)579 dm_table_deps_ioctl(prop_dictionary_t dm_dict)
580 {
581           dm_dev_t *dmv;
582           dm_table_t *tbl;
583           dm_table_entry_t *table_en;
584 
585           prop_array_t cmd_array;
586           const char *name, *uuid;
587           uint32_t flags, minor;
588 
589           int table_type;
590 
591           name = NULL;
592           uuid = NULL;
593           dmv = NULL;
594           flags = 0;
595 
596           prop_dictionary_get_cstring_nocopy(dm_dict, DM_IOCTL_NAME, &name);
597           prop_dictionary_get_cstring_nocopy(dm_dict, DM_IOCTL_UUID, &uuid);
598           prop_dictionary_get_uint32(dm_dict, DM_IOCTL_FLAGS, &flags);
599           prop_dictionary_get_uint32(dm_dict, DM_IOCTL_MINOR, &minor);
600 
601           /* create array for dev_t's */
602           cmd_array = prop_array_create();
603 
604           if ((dmv = dm_dev_lookup(name, uuid, minor)) == NULL) {
605                     dm_remove_flag(dm_dict, &flags, DM_EXISTS_FLAG);
606                     return ENOENT;
607           }
608           prop_dictionary_set_uint32(dm_dict, DM_IOCTL_MINOR, dmv->minor);
609           prop_dictionary_set_cstring(dm_dict, DM_IOCTL_NAME, dmv->name);
610           prop_dictionary_set_cstring(dm_dict, DM_IOCTL_UUID, dmv->uuid);
611 
612           dmdebug("Getting table deps for device: %s\n", dmv->name);
613 
614           /*
615            * if DM_QUERY_INACTIVE_TABLE_FLAG is passed we need to query
616            * INACTIVE TABLE
617            */
618           if (flags & DM_QUERY_INACTIVE_TABLE_FLAG)
619                     table_type = DM_TABLE_INACTIVE;
620           else
621                     table_type = DM_TABLE_ACTIVE;
622 
623           tbl = dm_table_get_entry(&dmv->table_head, table_type);
624 
625           TAILQ_FOREACH(table_en, tbl, next)
626                     dm_table_deps(table_en, cmd_array);
627 
628           dm_table_release(&dmv->table_head, table_type);
629           dm_dev_unbusy(dmv);
630 
631           prop_dictionary_set(dm_dict, DM_IOCTL_CMD_DATA, cmd_array);
632           prop_object_release(cmd_array);
633 
634           return 0;
635 }
636 
637 static int
dm_table_deps(dm_table_entry_t * table_en,prop_array_t array)638 dm_table_deps(dm_table_entry_t *table_en, prop_array_t array)
639 {
640           dm_mapping_t *map;
641           int i, size;
642           uint64_t ret, tmp;
643 
644           size = prop_array_count(array);
645 
646           TAILQ_FOREACH(map, &table_en->pdev_maps, next) {
647                     ret = map->data.pdev->udev;
648                     for (i = 0; i < size; i++) {
649                               if (prop_array_get_uint64(array, i, &tmp) == true)
650                                         if (ret == tmp)
651                                                   break; /* exists */
652                     }
653                     /*
654                      * Ignore if the device has already been added by
655                      * other tables.
656                      */
657                     if (i == size)
658                               prop_array_add_uint64(array, ret);
659           }
660 
661           return 0;
662 }
663 
664 /*
665  * Load new table/tables to device.
666  * Call apropriate target init routine open all physical pdev's and
667  * link them to device. For other targets mirror, strip, snapshot etc.
668  *
669  * Load table to inactive slot table are switched in dm_device_resume_ioctl.
670  * This simulates Linux behaviour better there should not be any difference.
671  */
672 int
dm_table_load_ioctl(prop_dictionary_t dm_dict)673 dm_table_load_ioctl(prop_dictionary_t dm_dict)
674 {
675           dm_dev_t *dmv;
676           dm_table_entry_t *table_en;
677           dm_table_t *tbl;
678           dm_target_t *target;
679 
680           prop_object_iterator_t iter;
681           prop_array_t cmd_array;
682           prop_dictionary_t target_dict;
683 
684           const char *name, *uuid, *type;
685 
686           uint32_t flags, ret, minor;
687 
688           char *str;
689 
690           ret = 0;
691           flags = 0;
692           name = NULL;
693           uuid = NULL;
694           dmv = NULL;
695           str = NULL;
696 
697           /*
698            * char *xml; xml = prop_dictionary_externalize(dm_dict);
699            * kprintf("%s\n",xml);
700            */
701 
702           prop_dictionary_get_cstring_nocopy(dm_dict, DM_IOCTL_NAME, &name);
703           prop_dictionary_get_cstring_nocopy(dm_dict, DM_IOCTL_UUID, &uuid);
704           prop_dictionary_get_uint32(dm_dict, DM_IOCTL_FLAGS, &flags);
705           prop_dictionary_get_uint32(dm_dict, DM_IOCTL_MINOR, &minor);
706 
707           cmd_array = prop_dictionary_get(dm_dict, DM_IOCTL_CMD_DATA);
708           iter = prop_array_iterator(cmd_array);
709           dm_dbg_print_flags(flags);
710 
711           if ((dmv = dm_dev_lookup(name, uuid, minor)) == NULL) {
712                     dm_remove_flag(dm_dict, &flags, DM_EXISTS_FLAG);
713                     return ENOENT;
714           }
715           dmdebug("Loading table to device: %s--%d\n", name,
716               dmv->table_head.cur_active_table);
717 
718           /*
719            * I have to check if this table slot is not used by another table list.
720            * if it is used I should free them.
721            */
722           if (dmv->flags & DM_INACTIVE_PRESENT_FLAG)
723                     dm_table_destroy(&dmv->table_head, DM_TABLE_INACTIVE);
724 
725           dm_dbg_print_flags(dmv->flags);
726           tbl = dm_table_get_entry(&dmv->table_head, DM_TABLE_INACTIVE);
727 
728           prop_dictionary_set_uint32(dm_dict, DM_IOCTL_MINOR, dmv->minor);
729 
730           while ((target_dict = prop_object_iterator_next(iter)) != NULL) {
731                     prop_dictionary_get_cstring_nocopy(target_dict,
732                         DM_TABLE_TYPE, &type);
733                     /*
734                      * If we want to deny table with 2 or more different
735                      * target we should do it here
736                      */
737                     if (((target = dm_target_lookup(type)) == NULL) &&
738                         ((target = dm_target_autoload(type)) == NULL)) {
739                               dm_table_release(&dmv->table_head, DM_TABLE_INACTIVE);
740                               dm_dev_unbusy(dmv);
741                               return ENOENT;
742                     }
743                     if ((table_en = kmalloc(sizeof(dm_table_entry_t),
744                                   M_DM, M_WAITOK)) == NULL) {
745                               dm_table_release(&dmv->table_head, DM_TABLE_INACTIVE);
746                               dm_dev_unbusy(dmv);
747                               dm_target_unbusy(target);
748                               return ENOMEM;
749                     }
750                     prop_dictionary_get_uint64(target_dict, DM_TABLE_START,
751                         &table_en->start);
752                     prop_dictionary_get_uint64(target_dict, DM_TABLE_LENGTH,
753                         &table_en->length);
754 
755                     dmdebug("table_en->start = %ju, table_en->length = %ju\n",
756                               (uintmax_t)table_en->start,
757                               (uintmax_t)table_en->length);
758 
759                     table_en->target = target;
760                     table_en->dev = dmv;
761                     table_en->target_config = NULL;
762                     TAILQ_INIT(&table_en->pdev_maps);
763 
764                     /*
765                      * There is a parameter string after dm_target_spec
766                      * structure which  points to /dev/wd0a 284 part of
767                      * table. String str points to this text. This can be
768                      * null and therefore it should be checked before we try to
769                      * use it.
770                      */
771                     prop_dictionary_get_cstring(target_dict,
772                         DM_TABLE_PARAMS, &str);
773 
774                     TAILQ_INSERT_TAIL(tbl, table_en, next);
775 
776                     /*
777                      * Params string is different for every target,
778                      * therfore I have to pass it to target init
779                      * routine and parse parameters there.
780                      */
781                     dmdebug("String passed in is: \"%s\"\n", str);
782 
783                     if ((ret = dm_table_init(target, table_en, str)) != 0) {
784                               dm_table_release(&dmv->table_head, DM_TABLE_INACTIVE);
785                               dm_table_destroy(&dmv->table_head, DM_TABLE_INACTIVE);
786                               kfree(str, M_TEMP);
787 
788                               dm_dev_unbusy(dmv);
789                               return ret;
790                     }
791                     kfree(str, M_TEMP);
792           }
793           prop_object_iterator_release(iter);
794 
795           dm_add_flag(dm_dict, &flags, DM_INACTIVE_PRESENT_FLAG);
796           atomic_set_int(&dmv->flags, DM_INACTIVE_PRESENT_FLAG);
797 
798           dm_table_release(&dmv->table_head, DM_TABLE_INACTIVE);
799 
800           dm_dev_unbusy(dmv);
801 #if 0
802           dmsetdiskinfo(dmv->diskp, &dmv->table_head);
803 #endif
804           return 0;
805 }
806 
807 static int
dm_table_init(dm_target_t * target,dm_table_entry_t * table_en,char * params)808 dm_table_init(dm_target_t *target, dm_table_entry_t *table_en, char *params)
809 {
810           int i, n, argc;
811           int ret = 0;
812           char **ap, **argv;
813 
814           if (params == NULL)
815                     return EINVAL;
816 
817           n = target->max_argc;
818           if (n) {
819                     dmdebug("Max argc %d for %s target\n", n, target->name);
820           } else {
821                     n = 20;  /* large enough slots for most targets */
822           }
823 
824           argv = kmalloc(sizeof(*argv) * n, M_DM, M_WAITOK | M_ZERO);
825 
826           for (ap = argv;
827                ap < &argv[n] && (*ap = strsep(&params, " \t")) != NULL;) {
828                     if (**ap != '\0')
829                               ap++;
830           }
831           argc = ap - argv;
832 
833           if (dm_debug_level) {
834                     for (i = 0; i < argc; i++)
835                               kprintf("DM: argv[%d] = \"%s\"\n", i, argv[i]);
836           }
837 
838           if (target->init)
839                     ret = target->init(table_en, argc, argv);
840 
841           kfree(argv, M_DM);
842 
843           return ret;
844 }
845 
846 /*
847  * Get description of all tables loaded to device from kernel
848  * and send it to libdevmapper.
849  *
850  * Output dictionary for every table:
851  *
852  * <key>cmd_data</key>
853  * <array>
854  *   <dict>
855  *    <key>type<key>
856  *    <string>...</string>
857  *
858  *    <key>start</key>
859  *    <integer>...</integer>
860  *
861  *    <key>length</key>
862  *    <integer>...</integer>
863  *
864  *    <key>params</key>
865  *    <string>...</string>
866  *   </dict>
867  * </array>
868  */
869 int
dm_table_status_ioctl(prop_dictionary_t dm_dict)870 dm_table_status_ioctl(prop_dictionary_t dm_dict)
871 {
872           dm_dev_t *dmv;
873           dm_table_t *tbl;
874           dm_table_entry_t *table_en;
875 
876           prop_array_t cmd_array;
877           prop_dictionary_t target_dict;
878 
879           uint32_t minor;
880 
881           const char *name, *uuid;
882           uint32_t flags;
883           int table_type;
884 
885           dmv = NULL;
886           uuid = NULL;
887           name = NULL;
888           flags = 0;
889 
890           prop_dictionary_get_cstring_nocopy(dm_dict, DM_IOCTL_NAME, &name);
891           prop_dictionary_get_cstring_nocopy(dm_dict, DM_IOCTL_UUID, &uuid);
892           prop_dictionary_get_uint32(dm_dict, DM_IOCTL_FLAGS, &flags);
893           prop_dictionary_get_uint32(dm_dict, DM_IOCTL_MINOR, &minor);
894 
895           cmd_array = prop_array_create();
896 
897           if ((dmv = dm_dev_lookup(name, uuid, minor)) == NULL) {
898                     dm_remove_flag(dm_dict, &flags, DM_EXISTS_FLAG);
899                     return ENOENT;
900           }
901           /*
902            * if DM_QUERY_INACTIVE_TABLE_FLAG is passed we need to query
903            * INACTIVE TABLE
904            */
905           if (flags & DM_QUERY_INACTIVE_TABLE_FLAG)
906                     table_type = DM_TABLE_INACTIVE;
907           else
908                     table_type = DM_TABLE_ACTIVE;
909 
910           if (dm_table_get_target_count(&dmv->table_head, DM_TABLE_ACTIVE))
911                     dm_add_flag(dm_dict, &flags, DM_ACTIVE_PRESENT_FLAG);
912           else {
913                     dm_remove_flag(dm_dict, &flags, DM_ACTIVE_PRESENT_FLAG);
914 
915                     if (dm_table_get_target_count(&dmv->table_head, DM_TABLE_INACTIVE))
916                               dm_add_flag(dm_dict, &flags, DM_INACTIVE_PRESENT_FLAG);
917                     else
918                               dm_remove_flag(dm_dict, &flags, DM_INACTIVE_PRESENT_FLAG);
919           }
920 
921           if (dmv->flags & DM_SUSPEND_FLAG)
922                     dm_add_flag(dm_dict, &flags, DM_SUSPEND_FLAG);
923 
924           prop_dictionary_set_uint32(dm_dict, DM_IOCTL_MINOR, dmv->minor);
925 
926           dmdebug("Status of device tables: %s--%d\n",
927               name, dmv->table_head.cur_active_table);
928 
929           tbl = dm_table_get_entry(&dmv->table_head, table_type);
930 
931           TAILQ_FOREACH(table_en, tbl, next) {
932                     target_dict = prop_dictionary_create();
933                     dmdebug("%016" PRIu64 ", length %016" PRIu64
934                         ", target %s\n", table_en->start, table_en->length,
935                         table_en->target->name);
936 
937                     prop_dictionary_set_uint64(target_dict, DM_TABLE_START,
938                         table_en->start);
939                     prop_dictionary_set_uint64(target_dict, DM_TABLE_LENGTH,
940                         table_en->length);
941 
942                     prop_dictionary_set_cstring(target_dict, DM_TABLE_TYPE,
943                         table_en->target->name);
944 
945                     /* dm_table_get_cur_actv.table ?? */
946                     prop_dictionary_set_int32(target_dict, DM_TABLE_STAT,
947                         dmv->table_head.cur_active_table);
948 
949                     dm_table_status(table_en, target_dict, flags);
950 
951                     prop_array_add(cmd_array, target_dict);
952                     prop_object_release(target_dict);
953           }
954 
955           dm_table_release(&dmv->table_head, table_type);
956           dm_dev_unbusy(dmv);
957 
958           prop_dictionary_set_uint32(dm_dict, DM_IOCTL_FLAGS, flags);
959           prop_dictionary_set(dm_dict, DM_IOCTL_CMD_DATA, cmd_array);
960           prop_object_release(cmd_array);
961 
962           return 0;
963 }
964 
965 static int
dm_table_status(dm_table_entry_t * table_en,prop_dictionary_t target_dict,uint32_t flags)966 dm_table_status(dm_table_entry_t *table_en,
967           prop_dictionary_t target_dict, uint32_t flags)
968 {
969           void *cfg;
970           char *params;
971           int is_table;
972 
973           cfg = table_en->target_config;
974           params = NULL;
975 
976           is_table = (flags & DM_STATUS_TABLE_FLAG) ? 1 : 0;
977 
978           if (is_table && table_en->target->table) {
979                     params = table_en->target->table(cfg);
980           } else if (!is_table && table_en->target->info) {
981                     params = table_en->target->info(cfg);
982           } else {
983                     prop_dictionary_set_cstring(target_dict, DM_TABLE_PARAMS, "");
984                     return ENOTSUP;
985           }
986 
987           if (params == NULL) {
988                     prop_dictionary_set_cstring(target_dict, DM_TABLE_PARAMS, "");
989                     return ENOMEM;
990           }
991 
992           prop_dictionary_set_cstring(target_dict, DM_TABLE_PARAMS, params);
993           kfree(params, M_DM);
994           return 0;
995 }
996 
997 int
dm_message_ioctl(prop_dictionary_t dm_dict)998 dm_message_ioctl(prop_dictionary_t dm_dict)
999 {
1000           dm_table_t  *tbl;
1001           dm_table_entry_t *table_en;
1002           dm_dev_t *dmv;
1003           const char *name, *uuid;
1004           uint32_t flags, minor;
1005           uint64_t table_start, table_end, sector;
1006           char *msg;
1007           int ret, found = 0;
1008 
1009           flags = 0;
1010           name = NULL;
1011           uuid = NULL;
1012 
1013           /* Get needed values from dictionary. */
1014           prop_dictionary_get_cstring_nocopy(dm_dict, DM_IOCTL_NAME, &name);
1015           prop_dictionary_get_cstring_nocopy(dm_dict, DM_IOCTL_UUID, &uuid);
1016           prop_dictionary_get_uint32(dm_dict, DM_IOCTL_FLAGS, &flags);
1017           prop_dictionary_get_uint32(dm_dict, DM_IOCTL_MINOR, &minor);
1018           prop_dictionary_get_uint64(dm_dict, DM_MESSAGE_SECTOR, &sector);
1019 
1020           dm_dbg_print_flags(flags);
1021 
1022           if ((dmv = dm_dev_lookup(name, uuid, minor)) == NULL) {
1023                     dm_remove_flag(dm_dict, &flags, DM_EXISTS_FLAG);
1024                     return ENOENT;
1025           }
1026 
1027           /* Get message string */
1028           prop_dictionary_get_cstring(dm_dict, DM_MESSAGE_STR, &msg);
1029 
1030           tbl = dm_table_get_entry(&dmv->table_head, DM_TABLE_ACTIVE);
1031 
1032           ret = EINVAL;
1033 
1034           if (sector == 0) {
1035                     if (!TAILQ_EMPTY(tbl)) {
1036                               table_en = TAILQ_FIRST(tbl);
1037                               found = 1;
1038                     }
1039           } else {
1040                     TAILQ_FOREACH(table_en, tbl, next) {
1041                               table_start = table_en->start;
1042                               table_end = table_start + table_en->length;
1043 
1044                               if ((sector >= table_start) && (sector < table_end)) {
1045                                         found = 1;
1046                                         break;
1047                               }
1048                     }
1049           }
1050 
1051           if (found) {
1052                     if (table_en->target->message != NULL)
1053                               ret = table_en->target->message(table_en, msg);
1054           }
1055 
1056           dm_table_release(&dmv->table_head, DM_TABLE_ACTIVE);
1057 
1058           kfree(msg, M_TEMP);
1059           dm_dev_unbusy(dmv);
1060 
1061           return ret;
1062 }
1063 
1064 /*
1065  * For every call I have to set kernel driver version.
1066  * Because I can have commands supported only in other
1067  * newer/later version. This routine is called for every
1068  * ioctl command.
1069  */
1070 int
dm_check_version(prop_dictionary_t dm_dict)1071 dm_check_version(prop_dictionary_t dm_dict)
1072 {
1073           size_t i;
1074           int dm_version[3];
1075           prop_array_t ver;
1076 
1077           ver = prop_dictionary_get(dm_dict, DM_IOCTL_VERSION);
1078 
1079           for (i = 0; i < 3; i++)
1080                     prop_array_get_uint32(ver, i, &dm_version[i]);
1081 
1082           if (DM_VERSION_MAJOR != dm_version[0] || DM_VERSION_MINOR < dm_version[1]) {
1083                     dmdebug("libdevmapper/kernel version mismatch "
1084                         "kernel: %d.%d.%d libdevmapper: %d.%d.%d\n",
1085                         DM_VERSION_MAJOR, DM_VERSION_MINOR, DM_VERSION_PATCHLEVEL,
1086                         dm_version[0], dm_version[1], dm_version[2]);
1087 
1088                     return EIO;
1089           }
1090           prop_array_set_uint32(ver, 0, DM_VERSION_MAJOR);
1091           prop_array_set_uint32(ver, 1, DM_VERSION_MINOR);
1092           prop_array_set_uint32(ver, 2, DM_VERSION_PATCHLEVEL);
1093 
1094           prop_dictionary_set(dm_dict, DM_IOCTL_VERSION, ver);
1095 
1096           return 0;
1097 }
1098