xref: /dragonfly/lib/libdm/dm_task.c (revision 5fb3968e)
1 /*
2  * Copyright (c) 2011 Alex Hornung <alex@alexhornung.com>.
3  * All rights reserved.
4  *
5  * This code is derived from software contributed to The NetBSD Foundation
6  * by Adam Hamsik.
7  *
8  * Redistribution and use in source and binary forms, with or without
9  * modification, are permitted provided that the following conditions
10  * are met:
11  *
12  * 1. Redistributions of source code must retain the above copyright
13  *    notice, this list of conditions and the following disclaimer.
14  * 2. Redistributions in binary form must reproduce the above copyright
15  *    notice, this list of conditions and the following disclaimer in
16  *    the documentation and/or other materials provided with the
17  *    distribution.
18  *
19  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
20  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
21  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
22  * FOR A PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE
23  * COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
24  * INCIDENTAL, SPECIAL, EXEMPLARY OR CONSEQUENTIAL DAMAGES (INCLUDING,
25  * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
26  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
27  * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
28  * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
29  * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
30  * SUCH DAMAGE.
31  */
32 #include <sys/types.h>
33 #include <sys/param.h>
34 #include <sys/ioctl.h>
35 #include <sys/stat.h>
36 #include <dev/disk/dm/netbsd-dm.h>
37 #include <stdlib.h>
38 #include <string.h>
39 #include <stdio.h>
40 #include <unistd.h>
41 #include <fcntl.h>
42 #include <errno.h>
43 
44 #include <libprop/proplib.h>
45 #include "libdm.h"
46 
47 struct dm_task {
48 	int			task_type;
49 	int			was_enoent;
50 	prop_dictionary_t	dict;
51 	void			*data_buffer;
52 };
53 
54 struct dm_cmd {
55 	int		task_type;
56 	const char	*dm_cmd;
57 	uint32_t	cmd_version[3];
58 };
59 
60 struct dm_cmd dm_cmds[] = {
61 	{ DM_DEVICE_REMOVE,		"remove",	{4, 0, 0} },
62 	{ DM_DEVICE_REMOVE_ALL,		"remove_all",	{4, 0, 0} },
63 	{ DM_DEVICE_CREATE,		"create",	{4, 0, 0} },
64 	{ DM_DEVICE_RELOAD,		"reload",	{4, 0, 0} },
65 	{ DM_DEVICE_RESUME,		"resume",	{4, 0, 0} },
66 	{ DM_DEVICE_SUSPEND,		"suspend",	{4, 0, 0} },
67 	{ DM_DEVICE_CLEAR,		"clear",	{4, 0, 0} },
68 	{ DM_DEVICE_LIST_VERSIONS,	"targets",	{4, 1, 0} },
69 	{ DM_DEVICE_STATUS,		"status",	{4, 0, 0} },
70 	{ DM_DEVICE_TABLE,		"table",	{4, 0, 0} },
71 	{ DM_DEVICE_INFO,		"info",		{4, 0, 0} },
72 	{ DM_DEVICE_DEPS,		"deps",		{4, 0, 0} },
73 	{ DM_DEVICE_VERSION,		"version",	{4, 0, 0} },
74 	{ DM_DEVICE_TARGET_MSG,		"message",	{4, 2, 0} },
75 	{ DM_DEVICE_RENAME,		"rename",	{4, 0, 0} },
76 	{ DM_DEVICE_LIST,		"names",	{4, 0, 0} },
77 	{ 0,				NULL,		{0, 0, 0} }
78 };
79 
80 #define _LOG_DEBUG	0
81 #define _LOG_WARN	5
82 #define _LOG_ERR	10
83 
84 static void _stderr_log(int level, const char *file,
85     int line, const char *fmt, ...)
86 {
87 	const char *prefix;
88 	__va_list ap;
89 
90 	switch (level) {
91 	case _LOG_DEBUG:
92 		prefix = "debug: ";
93 		break;
94 	case _LOG_WARN:
95 		prefix = "warning: ";
96 		break;
97 	case _LOG_ERR:
98 		prefix = "error: ";
99 		break;
100 	default:
101 		prefix = "";
102 	}
103 
104 	fprintf(stderr, "libdm %s:%d: ", file, line);
105 	fprintf(stderr, "%s", prefix);
106 
107 	__va_start(ap, fmt);
108 	vfprintf(stderr, fmt, ap);
109 	__va_end(ap);
110 
111 	fprintf(stderr, "\n");
112 
113 	return;
114 }
115 
116 static dm_error_func_t dm_log = _stderr_log;
117 
118 struct dm_task *
119 dm_task_create(int task_type)
120 {
121 	struct dm_task *dmt;
122 	struct dm_cmd *cmd = NULL;
123 	const char *task_cmd = NULL;
124 	prop_array_t pa;
125 	uint32_t flags = DM_EXISTS_FLAG;
126 	int i;
127 
128 	for (i = 0; dm_cmds[i].dm_cmd != NULL; i++) {
129 		if (dm_cmds[i].task_type == task_type) {
130 			cmd = &dm_cmds[i];
131 			task_cmd = dm_cmds[i].dm_cmd;
132 			break;
133 		}
134 	}
135 
136 	if (task_cmd == NULL)
137 		return NULL;
138 
139 	if (task_type == DM_DEVICE_TABLE)
140 		flags |= DM_STATUS_TABLE_FLAG;
141 
142 	if (task_type == DM_DEVICE_SUSPEND)
143 		flags |= DM_SUSPEND_FLAG;
144 
145 	if ((dmt = malloc(sizeof(*dmt))) == NULL)
146 		return NULL;
147 
148 	memset(dmt, 0, sizeof(*dmt));
149 
150 	dmt->task_type = task_type;
151 	dmt->was_enoent = 0;
152 
153 	if ((dmt->dict = prop_dictionary_create()) == NULL)
154 		goto err;
155 
156 	if ((pa = prop_array_create_with_capacity(3)) == NULL)
157 		goto err;
158 
159 	if (!prop_array_add_uint32(pa, cmd->cmd_version[0])) {
160 		prop_object_release(pa);
161 		goto err;
162 	}
163 
164 	if (!prop_array_add_uint32(pa, cmd->cmd_version[1])) {
165 		prop_object_release(pa);
166 		goto err;
167 	}
168 
169 	if (!prop_array_add_uint32(pa, cmd->cmd_version[2])) {
170 		prop_object_release(pa);
171 		goto err;
172 	}
173 
174 	if (!prop_dictionary_set(dmt->dict, DM_IOCTL_VERSION, pa)) {
175 		prop_object_release(pa);
176 		goto err;
177 	}
178 
179 	prop_object_release(pa);
180 
181 	if (!prop_dictionary_set_cstring(dmt->dict, DM_IOCTL_COMMAND,
182 	    task_cmd))
183 		goto err;
184 
185 	if (!prop_dictionary_set_uint32(dmt->dict, DM_IOCTL_FLAGS, flags))
186 		goto err;
187 
188 	if ((pa = prop_array_create_with_capacity(5)) == NULL)
189 		goto err;
190 
191 	if (!prop_dictionary_set(dmt->dict, DM_IOCTL_CMD_DATA, pa)) {
192 		prop_object_release(pa);
193 		goto err;
194 	}
195 
196 	prop_object_release(pa);
197 
198 	return dmt;
199 	/* NOT REACHED */
200 
201 err:
202 	if (dmt->dict != NULL)
203 		prop_object_release(dmt->dict);
204 	if (dmt)
205 		free(dmt);
206 
207 	return NULL;
208 }
209 
210 
211 void
212 dm_task_destroy(struct dm_task *dmt)
213 {
214 	if (dmt) {
215 		if (dmt->data_buffer)
216 			free(dmt->data_buffer);
217 
218 		if (dmt->dict) {
219 			prop_object_release(dmt->dict);
220 			dmt->dict = NULL;
221 		}
222 
223 		free(dmt);
224 	}
225 }
226 
227 int
228 dm_task_run(struct dm_task *dmt)
229 {
230 	struct dm_task *dmt_internal = NULL;
231 	prop_dictionary_t ret_pd = NULL;
232 	prop_array_t pa;
233 	int error;
234 	int fd;
235 	int need_unroll = 0;
236 
237 	if ((fd = open("/dev/mapper/control", O_RDWR)) < -1)
238 		goto err;
239 
240 	pa = prop_dictionary_get(dmt->dict, DM_IOCTL_CMD_DATA);
241 	if ((dmt->task_type == DM_DEVICE_CREATE) && (pa != NULL) &&
242 	    (prop_array_count(pa) > 0)) {
243 		/*
244 		 * Magic to separate a combined DM_DEVICE_CREATE+RELOAD int
245 		 * a DM_DEVICE_CREATE and a RELOAD with target table.
246 		 */
247 
248 		if ((dmt_internal = dm_task_create(DM_DEVICE_CREATE)) == NULL)
249 			goto err;
250 		if (!dm_task_set_name(dmt_internal, dm_task_get_name(dmt)))
251 			goto err;
252 		if (!dm_task_set_uuid(dmt_internal, dm_task_get_uuid(dmt)))
253 			goto err;
254 		if (!dm_task_run(dmt_internal))
255 			goto err;
256 		dm_task_destroy(dmt_internal);
257 		dmt_internal = NULL;
258 
259 		if (!prop_dictionary_set_cstring_nocopy(dmt->dict,
260 		    DM_IOCTL_COMMAND, "reload"))
261 			goto unroll;
262 		dmt->task_type = DM_DEVICE_RELOAD;
263 		if ((error = prop_dictionary_sendrecv_ioctl(dmt->dict, fd,
264 		    NETBSD_DM_IOCTL, &ret_pd)) != 0) {
265 			dm_log(_LOG_ERR, __FILE__, __LINE__, "ioctl failed: %d",
266 			    error);
267 			goto unroll;
268 		}
269 
270 		if (!prop_dictionary_set_cstring_nocopy(dmt->dict,
271 		    DM_IOCTL_COMMAND, "resume"))
272 			goto unroll;
273 		dmt->task_type = DM_DEVICE_RESUME;
274 		/* Remove superfluous stuff */
275 		prop_dictionary_remove(dmt->dict, DM_IOCTL_CMD_DATA);
276 
277 		need_unroll = 1;
278 	}
279 
280 	if ((error = prop_dictionary_sendrecv_ioctl(dmt->dict, fd,
281 	    NETBSD_DM_IOCTL, &ret_pd)) != 0) {
282 		if (((error == ENOENT) &&
283 		    ((dmt->task_type == DM_DEVICE_INFO) ||
284 		    (dmt->task_type == DM_DEVICE_STATUS)))) {
285 			dmt->was_enoent = 1;
286 			ret_pd = NULL;
287 		} else {
288 			dm_log(_LOG_ERR, __FILE__, __LINE__, "ioctl failed: %d",
289 			    error);
290 			if (need_unroll)
291 				goto unroll;
292 			else
293 				goto err;
294 		}
295 	}
296 
297 	if (ret_pd)
298 		prop_object_retain(ret_pd);
299 
300 	prop_object_release(dmt->dict);
301 	dmt->dict = ret_pd;
302 
303 	return 1;
304 	/* NOT REACHED */
305 
306 unroll:
307 	prop_dictionary_remove(dmt->dict, DM_IOCTL_CMD_DATA);
308 
309 	if (!prop_dictionary_set_cstring_nocopy(dmt->dict, DM_IOCTL_COMMAND,
310 	    "remove")) {
311 		dm_log(_LOG_ERR, __FILE__, __LINE__, "couldn't unroll changes "
312 		    "in dm_task_run");
313 		goto err;
314 	}
315 
316 	if ((error = prop_dictionary_sendrecv_ioctl(dmt->dict, fd,
317 	    NETBSD_DM_IOCTL, &ret_pd)) != 0) {
318 		dm_log(_LOG_ERR, __FILE__, __LINE__, "ioctl failed: %d",
319 		    error);
320 		goto unroll;
321 	}
322 	dmt->task_type = DM_DEVICE_REMOVE;
323 	dm_task_run(dmt);
324 
325 err:
326 	if (fd >= 0)
327 		close(fd);
328 
329 	if (dmt_internal)
330 		dm_task_destroy(dmt_internal);
331 
332 	return 0;
333 }
334 
335 int
336 dm_task_set_name(struct dm_task *dmt, const char *name)
337 {
338 	return prop_dictionary_set_cstring(dmt->dict, DM_IOCTL_NAME,
339 	    __DECONST(char *, name));
340 }
341 
342 
343 const char *
344 dm_task_get_name(struct dm_task *dmt)
345 {
346 	const char *name = NULL;
347 
348 	prop_dictionary_get_cstring_nocopy(dmt->dict, DM_IOCTL_NAME, &name);
349 
350 	return name;
351 }
352 
353 int
354 dm_task_set_newname(struct dm_task *dmt, const char *newname)
355 {
356 	return prop_dictionary_set_cstring(dmt->dict, DM_DEV_NEWNAME,
357 	    __DECONST(char *, newname));
358 }
359 
360 int
361 dm_task_set_major(struct dm_task *dmt __unused, int major __unused)
362 {
363 	return 1;
364 }
365 
366 int
367 dm_task_set_minor(struct dm_task *dmt, int minor)
368 {
369 	return prop_dictionary_set_int32(dmt->dict, DM_IOCTL_MINOR, minor);
370 }
371 
372 int
373 dm_task_get_minor(struct dm_task *dmt)
374 {
375 	int minor = 0;
376 
377 	minor = prop_dictionary_get_int32(dmt->dict, DM_IOCTL_MINOR, &minor);
378 
379 	return minor;
380 }
381 
382 int
383 dm_task_set_uuid(struct dm_task *dmt, const char *uuid)
384 {
385 	return prop_dictionary_set_cstring(dmt->dict, DM_IOCTL_UUID,
386 	    __DECONST(char *,uuid));
387 }
388 
389 const char *
390 dm_task_get_uuid(struct dm_task *dmt)
391 {
392 	const char *uuid = NULL;
393 
394 	prop_dictionary_get_cstring_nocopy(dmt->dict, DM_IOCTL_UUID, &uuid);
395 
396 	return uuid;
397 }
398 
399 int
400 dm_task_add_target(struct dm_task *dmt, uint64_t start, size_t size,
401     const char *target, const char *params)
402 {
403 	prop_dictionary_t target_dict = NULL;
404 	prop_array_t pa = NULL;
405 
406 	if ((pa = prop_dictionary_get(dmt->dict, DM_IOCTL_CMD_DATA)) == NULL)
407 		return 0;
408 
409 	if ((target_dict = prop_dictionary_create()) == NULL)
410 		return 0;
411 
412 	if (!prop_dictionary_set_uint64(target_dict, DM_TABLE_START, start))
413 		goto err;
414 
415 	if (!prop_dictionary_set_uint64(target_dict, DM_TABLE_LENGTH, size))
416 		goto err;
417 
418 	if (!prop_dictionary_set_cstring(target_dict, DM_TABLE_TYPE, target))
419 		goto err;
420 
421 	if (!prop_dictionary_set_cstring(target_dict, DM_TABLE_PARAMS, params))
422 		goto err;
423 
424 	if (!prop_array_add(pa, target_dict))
425 		goto err;
426 
427 	prop_object_release(target_dict);
428 
429 	return 1;
430 	/* NOT REACHED */
431 
432 err:
433 	prop_object_release(target_dict);
434 	return 0;
435 }
436 
437 int
438 dm_task_set_sector(struct dm_task *dmt, uint64_t sector)
439 {
440 	return prop_dictionary_set_uint64(dmt->dict, DM_MESSAGE_SECTOR,
441 	    sector);
442 }
443 
444 int
445 dm_task_set_message(struct dm_task *dmt, const char *msg)
446 {
447 	return prop_dictionary_set_cstring(dmt->dict, DM_MESSAGE_STR, msg);
448 }
449 
450 int
451 dm_task_set_ro(struct dm_task *dmt)
452 {
453 	uint32_t flags = 0;
454 
455 	prop_dictionary_get_uint32(dmt->dict, DM_IOCTL_FLAGS, &flags);
456 	flags |= DM_READONLY_FLAG;
457 
458 	return prop_dictionary_set_uint32(dmt->dict, DM_IOCTL_FLAGS, flags);
459 }
460 
461 int
462 dm_task_no_open_count(struct dm_task *dmt __unused)
463 {
464 	/*
465 	 * nothing else needed, since we don't have performance problems when
466 	 * getting the open count.
467 	 */
468 	return 1;
469 }
470 
471 int
472 dm_task_query_inactive_table(struct dm_task *dmt)
473 {
474 	uint32_t flags = 0;
475 
476 	prop_dictionary_get_uint32(dmt->dict, DM_IOCTL_FLAGS, &flags);
477 	flags |= DM_QUERY_INACTIVE_TABLE_FLAG;
478 
479 	return prop_dictionary_set_uint32(dmt->dict, DM_IOCTL_FLAGS, flags);
480 }
481 
482 int
483 dm_task_set_read_ahead(struct dm_task *dmt __unused,
484     uint32_t read_ahead __unused)
485 {
486 	/* We don't support readahead */
487 	return 1;
488 }
489 
490 int
491 dm_task_get_read_ahead(struct dm_task *dmt __unused, uint32_t *read_ahead)
492 {
493 	*read_ahead = 0;
494 
495 	return 1;
496 }
497 
498 int
499 dm_task_secure_data(struct dm_task *dmt)
500 {
501 	/* XXX: needs kernel support */
502 	uint32_t flags = 0;
503 
504 	prop_dictionary_get_uint32(dmt->dict, DM_IOCTL_FLAGS, &flags);
505 	flags |= DM_SECURE_DATA_FLAG;
506 
507 	return prop_dictionary_set_uint32(dmt->dict, DM_IOCTL_FLAGS, flags);
508 }
509 
510 int
511 dm_task_get_info(struct dm_task *dmt, struct dm_info *dmi)
512 {
513 	uint32_t flags = 0;
514 
515 	memset(dmi, 0, sizeof(struct dm_info));
516 
517 	/* Hack due to the way Linux dm works */
518 	if (dmt->was_enoent) {
519 		dmi->exists = 0;
520 		return 1;
521 		/* NOT REACHED */
522 	}
523 
524 	if (!prop_dictionary_get_uint32(dmt->dict, DM_IOCTL_FLAGS,
525 	    &flags))
526 		return 0;
527 
528 	prop_dictionary_get_int32(dmt->dict, DM_IOCTL_OPEN, &dmi->open_count);
529 
530 	prop_dictionary_get_uint32(dmt->dict, DM_IOCTL_TARGET_COUNT,
531 	    &dmi->target_count);
532 
533 	prop_dictionary_get_uint32(dmt->dict, DM_IOCTL_EVENT, &dmi->event_nr);
534 
535 	prop_dictionary_get_uint32(dmt->dict, DM_IOCTL_MINOR, &dmi->minor);
536 
537 	dmi->major = dm_get_major();
538 
539 	dmi->read_only = (flags & DM_READONLY_FLAG);
540 	dmi->exists = (flags & DM_EXISTS_FLAG);
541 	dmi->suspended = (flags & DM_SUSPEND_FLAG);
542 	dmi->live_table = (flags & DM_ACTIVE_PRESENT_FLAG);
543 	dmi->inactive_table = (flags & DM_INACTIVE_PRESENT_FLAG);
544 
545 	return 1;
546 }
547 
548 int
549 dm_task_get_driver_version(struct dm_task *dmt, char *ver, size_t ver_sz)
550 {
551 	prop_array_t pa_ver;
552 	uint32_t maj = 0, min = 0, patch = 0;
553 
554 	if ((pa_ver = prop_dictionary_get(dmt->dict, DM_IOCTL_VERSION)) == NULL)
555 		return 0;
556 
557 	if (!prop_array_get_uint32(pa_ver, 0, &maj))
558 		return 0;
559 
560 	if (!prop_array_get_uint32(pa_ver, 1, &min))
561 		return 0;
562 
563 	if (!prop_array_get_uint32(pa_ver, 2, &patch))
564 		return 0;
565 
566 	snprintf(ver, ver_sz, "%u.%u.%u", maj, min, patch);
567 
568 	return 1;
569 }
570 
571 struct dm_deps *
572 dm_task_get_deps(struct dm_task *dmt)
573 {
574 	prop_object_iterator_t iter;
575 	prop_array_t pa;
576 	prop_object_t po;
577 	struct dm_deps *deps;
578 
579 	unsigned int count;
580 	int i;
581 
582 	if ((pa = prop_dictionary_get(dmt->dict, DM_IOCTL_CMD_DATA)) == NULL)
583 		return NULL;
584 
585 	count = prop_array_count(pa);
586 
587 	if (dmt->data_buffer != NULL)
588 		free(dmt->data_buffer);
589 
590 	if ((dmt->data_buffer = malloc(sizeof(struct dm_deps) +
591 	    (count * sizeof(uint64_t)))) == NULL)
592 		return NULL;
593 
594 	if ((iter = prop_array_iterator(pa)) == NULL)
595 		return NULL;
596 
597 	deps = (struct dm_deps *)dmt->data_buffer;
598 	memset(deps, 0, sizeof(struct dm_deps) + (count * sizeof(uint64_t)));
599 	i = 0;
600 	while ((po = prop_object_iterator_next(iter)) != NULL)
601 		deps->deps[i++] = prop_number_unsigned_integer_value(po);
602 
603 	deps->count = (uint32_t)count;
604 
605 	prop_object_iterator_release(iter);
606 
607 	return deps;
608 }
609 
610 struct dm_versions *
611 dm_task_get_versions(struct dm_task *dmt)
612 {
613 	prop_object_iterator_t iter;
614 	prop_dictionary_t target_dict;
615 	prop_array_t pa, pa_ver;
616 	struct dm_versions *vers;
617 
618 	unsigned int count;
619 	int i, j;
620 
621 	if ((pa = prop_dictionary_get(dmt->dict, DM_IOCTL_CMD_DATA)) == NULL)
622 		return NULL;
623 
624 	count = prop_array_count(pa);
625 
626 	if (dmt->data_buffer != NULL)
627 		free(dmt->data_buffer);
628 
629 	if ((dmt->data_buffer = malloc(sizeof(struct dm_versions) * count))
630 	    == NULL)
631 		return NULL;
632 
633 	if ((iter = prop_array_iterator(pa)) == NULL)
634 		return NULL;
635 
636 	vers = (struct dm_versions *)dmt->data_buffer;
637 	memset(vers, 0, sizeof(struct dm_versions) * count);
638 	i = 0;
639 	while ((target_dict = prop_object_iterator_next(iter)) != NULL) {
640 		vers[i].next = sizeof(struct dm_versions);
641 		prop_dictionary_get_cstring_nocopy(target_dict,
642 		    DM_TARGETS_NAME, &vers[i].name);
643 
644 		pa_ver = prop_dictionary_get(target_dict, DM_TARGETS_VERSION);
645 		for (j = 0; j < 3; j++)
646 			prop_array_get_uint32(pa_ver, j, &vers[i].version[j]);
647 
648 		++i;
649 	}
650 
651 	/* Finish the array */
652 	vers[i-1].next = 0;
653 
654 	prop_object_iterator_release(iter);
655 
656 	return (struct dm_versions *)dmt->data_buffer;
657 }
658 
659 struct dm_names *
660 dm_task_get_names(struct dm_task *dmt)
661 {
662 	prop_object_iterator_t iter;
663 	prop_dictionary_t devs_dict;
664 	prop_array_t pa;
665 	struct dm_names *names;
666 
667 	unsigned int count;
668 	int i;
669 
670 	if ((pa = prop_dictionary_get(dmt->dict, DM_IOCTL_CMD_DATA)) == NULL)
671 		return NULL;
672 
673 	count = prop_array_count(pa);
674 
675 	if (dmt->data_buffer != NULL)
676 		free(dmt->data_buffer);
677 
678 	if ((dmt->data_buffer = malloc(sizeof(struct dm_names) * count))
679 	    == NULL)
680 		return NULL;
681 
682 	if ((iter = prop_array_iterator(pa)) == NULL)
683 		return NULL;
684 
685 	names = (struct dm_names *)dmt->data_buffer;
686 	memset(names, 0, sizeof(struct dm_names) * count);
687 	i = 0;
688 	while ((devs_dict = prop_object_iterator_next(iter)) != NULL) {
689 		names[i].next = sizeof(struct dm_names);
690 
691 		prop_dictionary_get_cstring_nocopy(devs_dict,
692 		    DM_DEV_NAME, &names[i].name);
693 
694 		prop_dictionary_get_uint64(devs_dict, DM_DEV_DEV,
695 		    &names[i].dev);
696 
697 		++i;
698 	}
699 
700 	/* Finish the array */
701 	names[i-1].next = 0;
702 
703 	prop_object_iterator_release(iter);
704 
705 	return (struct dm_names *)dmt->data_buffer;
706 }
707 
708 int
709 dm_task_update_nodes(void)
710 {
711 
712 	/* nothing else needed */
713 	return 1;
714 }
715 
716 void *
717 dm_get_next_target(struct dm_task *dmt, void *cur, uint64_t *startp,
718     uint64_t *lengthp, char **target_type, char **params)
719 {
720 	prop_object_iterator_t  iter;
721 	prop_dictionary_t target_dict;
722 	prop_array_t pa;
723 	uint64_t ulength;
724 	unsigned int count;
725 
726 	if ((pa = prop_dictionary_get(dmt->dict, DM_IOCTL_CMD_DATA)) == NULL)
727 		return NULL;
728 
729 	count = prop_array_count(pa);
730 
731 	if (cur == NULL) {
732 		if ((iter = prop_array_iterator(pa)) == NULL)
733 			return NULL;
734 	} else {
735 		iter = (prop_object_iterator_t)cur;
736 	}
737 
738 	/* Get the next target dict */
739 	if ((target_dict = prop_object_iterator_next(iter)) == NULL) {
740 		/* If there are no more target dicts, release the iterator */
741 		goto err;
742 	}
743 
744 	if (!prop_dictionary_get_cstring_nocopy(target_dict, DM_TABLE_TYPE,
745 	    (const char **)target_type))
746 		goto err;
747 
748 	/*
749 	 * Ugly __DECONST and (const char **) casts due to the linux prototype
750 	 * of this function.
751 	 */
752 	*params = __DECONST(char *, "");
753 	prop_dictionary_get_cstring_nocopy(target_dict, DM_TABLE_PARAMS,
754 	    (const char **)params);
755 
756 	if (!prop_dictionary_get_uint64(target_dict, DM_TABLE_START, startp))
757 		goto err;
758 
759 	if (!prop_dictionary_get_uint64(target_dict, DM_TABLE_LENGTH, &ulength))
760 		goto err;
761 
762 	*lengthp = (size_t)ulength;
763 
764 	/* If we are at the last element, make sure we return NULL */
765 	if (target_dict == prop_array_get(pa, count-1))
766 		goto err;
767 
768 	return (void *)iter;
769 	/* NOT REACHED */
770 
771 err:
772 	if (iter != NULL)
773 		prop_object_iterator_release(iter);
774 
775 	return NULL;
776 }
777 
778 uint32_t
779 dm_get_major(void)
780 {
781 	struct stat sb;
782 
783 	if (stat("/dev/mapper/control", &sb) < 0)
784 		return 0;
785 
786 	return (uint32_t)major(sb.st_dev);
787 }
788 
789 int
790 dm_is_dm_major(uint32_t major)
791 {
792 	return (major == dm_get_major());
793 }
794 
795 const char *
796 dm_dir(void)
797 {
798 	return "/dev/mapper";
799 }
800 
801 void
802 dm_udev_set_sync_support(int sync_udev __unused)
803 {
804 	return;
805 }
806 
807 int
808 dm_task_set_cookie(struct dm_task *dmt __unused, uint32_t *cookie __unused,
809     uint16_t udev_flags __unused)
810 {
811 	return 1;
812 }
813 
814 int
815 dm_udev_wait(uint32_t cookie __unused)
816 {
817 	return 1;
818 }
819 
820 void
821 dm_lib_release(void)
822 {
823 	return;
824 }
825 
826 int
827 dm_log_init(dm_error_func_t fn)
828 {
829 	if (fn)
830 		dm_log = fn;
831 	return 1;
832 }
833 
834 int
835 dm_log_init_verbose(int verbose __unused)
836 {
837 	return 1;
838 }
839 
840 /* XXX: unused in kernel */
841 int
842 dm_task_set_uid(struct dm_task *dmt, uid_t uid)
843 {
844 	return prop_dictionary_set_uint32(dmt->dict, DM_DEV_UID,
845 	    (uint32_t)uid);
846 }
847 
848 int
849 dm_task_set_gid(struct dm_task *dmt, gid_t gid)
850 {
851 	return prop_dictionary_set_uint32(dmt->dict, DM_DEV_GID,
852 	    (uint32_t)gid);
853 }
854 
855 int
856 dm_task_set_mode(struct dm_task *dmt, mode_t mode)
857 {
858 	return prop_dictionary_set_uint32(dmt->dict, DM_DEV_MODE,
859 	    (uint32_t)mode);
860 }
861 
862 int
863 dm_task_no_flush(struct dm_task *dmt __unused)
864 {
865 	uint32_t flags = 0;
866 
867 	prop_dictionary_get_uint32(dmt->dict, DM_IOCTL_FLAGS, &flags);
868 	flags |= DM_NOFLUSH_FLAG;
869 
870 	return prop_dictionary_set_uint32(dmt->dict, DM_IOCTL_FLAGS, flags);
871 }
872 
873 int
874 dm_task_skip_lockfs(struct dm_task *dmt __unused)
875 {
876 	uint32_t flags = 0;
877 
878 	prop_dictionary_get_uint32(dmt->dict, DM_IOCTL_FLAGS, &flags);
879 	flags |= DM_SKIP_LOCKFS_FLAG;
880 
881 	return prop_dictionary_set_uint32(dmt->dict, DM_IOCTL_FLAGS, flags);
882 }
883 
884 int dm_task_set_geometry(struct dm_task *dmt __unused,
885     const char *cylinders __unused, const char *heads __unused,
886     const char *sectors __unused, const char *start __unused)
887 {
888 	return 1;
889 }
890 
891 /*****************************************************************************/
892 /********************** DragonFly-specific extensions ************************/
893 /*****************************************************************************/
894 void *
895 dm_get_next_version(struct dm_task *dmt, void *cur, const char **target_type,
896     uint32_t *target_ver)
897 {
898 	prop_object_iterator_t iter;
899 	prop_dictionary_t target_dict;
900 	prop_array_t pa, pa_ver;
901 	unsigned int count;
902 	int j;
903 
904 	if ((pa = prop_dictionary_get(dmt->dict, DM_IOCTL_CMD_DATA)) == NULL)
905 		return NULL;
906 
907 	count = prop_array_count(pa);
908 
909 	if (cur == NULL) {
910 		if ((iter = prop_array_iterator(pa)) == NULL)
911 			return NULL;
912 	} else {
913 		iter = (prop_object_iterator_t)cur;
914 	}
915 
916 	/* Get the next target dict */
917 	if ((target_dict = prop_object_iterator_next(iter)) == NULL) {
918 		/* If there are no more target dicts, release the iterator */
919 		goto err;
920 	}
921 
922 	if (!prop_dictionary_get_cstring_nocopy(target_dict, DM_TARGETS_NAME,
923 	    target_type))
924 		goto err;
925 
926 	if ((pa_ver = prop_dictionary_get(target_dict, DM_TARGETS_VERSION))
927 	    == NULL)
928 		goto err;
929 
930 	for (j = 0; j < 3; j++) {
931 		if (!prop_array_get_uint32(pa_ver, j, &target_ver[j]))
932 			goto err;
933 	}
934 
935 	/* If we are at the last element, make sure we return NULL */
936 	if (target_dict == prop_array_get(pa, count-1))
937 		goto err;
938 
939 	return (void *)iter;
940 	/* NOT REACHED */
941 
942 err:
943 	if (iter != NULL)
944 		prop_object_iterator_release(iter);
945 
946 	return NULL;
947 }
948 
949 void *
950 dm_get_next_dep(struct dm_task *dmt, void *cur, uint64_t *dep)
951 {
952 	prop_object_iterator_t iter;
953 	prop_object_t po;
954 	prop_array_t pa;
955 	unsigned int count;
956 
957 	*dep = 0;
958 
959 	if ((pa = prop_dictionary_get(dmt->dict, DM_IOCTL_CMD_DATA)) == NULL)
960 		return NULL;
961 
962 	count = prop_array_count(pa);
963 
964 	if (cur == NULL) {
965 		if ((iter = prop_array_iterator(pa)) == NULL)
966 			return NULL;
967 	} else {
968 		iter = (prop_object_iterator_t)cur;
969 	}
970 
971 	/* Get the next target dict */
972 	if ((po = prop_object_iterator_next(iter)) == NULL) {
973 		/* If there are no more target dicts, release the iterator */
974 		goto err;
975 	}
976 
977 	*dep = prop_number_unsigned_integer_value(po);
978 
979 	/* If we are at the last element, make sure we return NULL */
980 	if (po == prop_array_get(pa, count-1))
981 		goto err;
982 
983 	return (void *)iter;
984 	/* NOT REACHED */
985 
986 err:
987 	if (iter != NULL)
988 		prop_object_iterator_release(iter);
989 
990 	return NULL;
991 }
992 
993 void *
994 dm_get_next_name(struct dm_task *dmt, void *cur, const char **name,
995     uint64_t *dev)
996 {
997 	prop_object_iterator_t iter;
998 	prop_dictionary_t devs_dict;
999 	prop_array_t pa;
1000 	unsigned int count;
1001 
1002 	if ((pa = prop_dictionary_get(dmt->dict, DM_IOCTL_CMD_DATA)) == NULL)
1003 		return NULL;
1004 
1005 	count = prop_array_count(pa);
1006 
1007 
1008 	if (cur == NULL) {
1009 		if ((iter = prop_array_iterator(pa)) == NULL)
1010 			return NULL;
1011 	} else {
1012 		iter = (prop_object_iterator_t)cur;
1013 	}
1014 
1015 	/* Get the next dev dict */
1016 	if ((devs_dict = prop_object_iterator_next(iter)) == NULL) {
1017 		/* If there are no more dev dicts, release the iterator */
1018 		goto err;
1019 	}
1020 
1021 	if (!prop_dictionary_get_cstring_nocopy(devs_dict, DM_DEV_NAME, name))
1022 		goto err;
1023 
1024 	if (!prop_dictionary_get_uint64(devs_dict, DM_DEV_DEV, dev))
1025 		goto err;
1026 
1027 	/* If we are at the last element, make sure we return NULL */
1028 	if (devs_dict == prop_array_get(pa, count-1))
1029 		goto err;
1030 
1031 	return (void *)iter;
1032 	/* NOT REACHED */
1033 
1034 err:
1035 	if (iter != NULL)
1036 		prop_object_iterator_release(iter);
1037 
1038 	return NULL;
1039 
1040 }
1041