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