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