xref: /netbsd-src/external/gpl2/lvm2/dist/libdm/ioctl/libdm-nbsd-iface.c (revision 10ad5ffa714ce1a679dcc9dd8159648df2d67b5a)
1 /*      $NetBSD: libdm-nbsd-iface.c,v 1.2 2009/06/05 19:57:25 haad Exp $        */
2 
3 /*
4  * Copyright (C) 2001-2004 Sistina Software, Inc. All rights reserved.
5  * Copyright (C) 2004-2007 Red Hat, Inc. All rights reserved.
6  * Copyright (C) 2008 Adam Hamsik. All rights reserved.
7  *
8  * This file is part of the device-mapper userspace tools.
9  *
10  * This copyrighted material is made available to anyone wishing to use,
11  * modify, copy, or redistribute it subject to the terms and conditions
12  * of the GNU Lesser General Public License v.2.1.
13  *
14  * You should have received a copy of the GNU Lesser General Public License
15  * along with this program; if not, write to the Free Software Foundation,
16  * Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
17  */
18 
19 #include "dmlib.h"
20 #include "libdm-targets.h"
21 #include "libdm-common.h"
22 
23 #include <sys/ioctl.h>
24 #include <sys/sysctl.h>
25 
26 #include <fcntl.h>
27 #include <dirent.h>
28 #include <limits.h>
29 
30 #include <netbsd-dm.h>
31 
32 #include <dm-ioctl.h>
33 
34 /*
35  * Ensure build compatibility.
36  * The hard-coded versions here are the highest present
37  * in the _cmd_data arrays.
38  */
39 
40 #if !((DM_VERSION_MAJOR == 1 && DM_VERSION_MINOR >= 0) || \
41       (DM_VERSION_MAJOR == 4 && DM_VERSION_MINOR >= 0))
42 #error The version of dm-ioctl.h included is incompatible.
43 #endif
44 
45 /* dm major version no for running kernel */
46 static unsigned _dm_version_minor = 0;
47 static unsigned _dm_version_patchlevel = 0;
48 
49 static int _control_fd = -1;
50 static int _version_checked = 0;
51 static int _version_ok = 1;
52 static unsigned _ioctl_buffer_double_factor = 0;
53 
54 /* *INDENT-OFF* */
55 
56 /*
57  * XXX Remove this structure and write another own one
58  * I don't understand why ioctl calls has different
59  * names then dm task type
60  */
61 static struct cmd_data _cmd_data_v4[] = {
62 	{"create",	DM_DEV_CREATE,		{4, 0, 0}},
63 	{"reload",	DM_TABLE_LOAD,		{4, 0, 0}}, /* DM_DEVICE_RELOAD */
64 	{"remove",	DM_DEV_REMOVE,		{4, 0, 0}},
65 	{"remove_all",	DM_REMOVE_ALL,		{4, 0, 0}},
66 	{"suspend",	DM_DEV_SUSPEND,		{4, 0, 0}},
67 	{"resume",	DM_DEV_SUSPEND,		{4, 0, 0}},
68 	{"info",	DM_DEV_STATUS,		{4, 0, 0}},
69 	{"deps",	DM_TABLE_DEPS,		{4, 0, 0}}, /* DM_DEVICE_DEPS */
70 	{"rename",	DM_DEV_RENAME,		{4, 0, 0}},
71 	{"version",	DM_VERSION,		{4, 0, 0}},
72 	{"status",	DM_TABLE_STATUS,	{4, 0, 0}},
73 	{"table",	DM_TABLE_STATUS,	{4, 0, 0}}, /* DM_DEVICE_TABLE */
74 	{"waitevent",	DM_DEV_WAIT,		{4, 0, 0}},
75 	{"names",	DM_LIST_DEVICES,	{4, 0, 0}},
76 	{"clear",	DM_TABLE_CLEAR,		{4, 0, 0}},
77 	{"mknodes",	DM_DEV_STATUS,		{4, 0, 0}},
78 #ifdef DM_LIST_VERSIONS
79 	{"targets",	DM_LIST_VERSIONS,	{4, 1, 0}},
80 #endif
81 #ifdef DM_TARGET_MSG
82 	{"message",	DM_TARGET_MSG,		{4, 2, 0}},
83 #endif
84 #ifdef DM_DEV_SET_GEOMETRY
85 	{"setgeometry",	DM_DEV_SET_GEOMETRY,	{4, 6, 0}},
86 #endif
87 };
88 /* *INDENT-ON* */
89 
90 /*
91  * In NetBSD we use sysctl to get kernel drivers info. control device
92  * has predefined minor number 0 and major number = char major number
93  * of dm driver. First slot is therefore ocupied with control device
94  * and minor device starts from 1;
95  */
96 
97 static int _control_device_number(uint32_t *major, uint32_t *minor)
98 {
99 
100 	nbsd_get_dm_major(major, DM_CHAR_MAJOR);
101 
102 	*minor = 0;
103 
104 	return 1;
105 }
106 
107 /*
108  * Returns 1 if exists; 0 if it doesn't; -1 if it's wrong
109  */
110 static int _control_exists(const char *control, uint32_t major, uint32_t minor)
111 {
112 	struct stat buf;
113 
114 	if (stat(control, &buf) < 0) {
115 		if (errno != ENOENT)
116 			log_sys_error("stat", control);
117 		return 0;
118 	}
119 
120 	if (!S_ISCHR(buf.st_mode)) {
121 		log_verbose("%s: Wrong inode type", control);
122 		if (!unlink(control))
123 			return 0;
124 		log_sys_error("unlink", control);
125 		return -1;
126 	}
127 
128 	if (major && buf.st_rdev != MKDEV(major, minor)) {
129 		log_verbose("%s: Wrong device number: (%u, %u) instead of "
130 			    "(%u, %u)", control,
131 			    MAJOR(buf.st_mode), MINOR(buf.st_mode),
132 			    major, minor);
133 		if (!unlink(control))
134 			return 0;
135 		log_sys_error("unlink", control);
136 		return -1;
137 	}
138 
139 	return 1;
140 }
141 
142 static int _create_control(const char *control, uint32_t major, uint32_t minor)
143 {
144 	int ret;
145 	mode_t old_umask;
146 
147 	if (!major)
148 		return 0;
149 
150 	old_umask = umask(0022);
151 	ret = dm_create_dir(dm_dir());
152 	umask(old_umask);
153 
154 	if (!ret)
155 		return 0;
156 
157 	log_verbose("Creating device %s (%u, %u)", control, major, minor);
158 
159 	if (mknod(control, S_IFCHR | S_IRUSR | S_IWUSR,
160 		  MKDEV(major, minor)) < 0)  {
161 		log_sys_error("mknod", control);
162 		return 0;
163 	}
164 
165 
166 	return 1;
167 }
168 
169 /* Check if major is device-mapper block device major number */
170 int dm_is_dm_major(uint32_t major)
171 {
172 	uint32_t dm_major;
173 
174 	nbsd_get_dm_major(&dm_major, DM_BLOCK_MAJOR);
175 
176 	if (major == dm_major)
177 		return 1;
178 
179 	return 0;
180 }
181 
182 /* Open control device if doesn't exist create it. */
183 static int _open_control(void)
184 {
185 	char control[PATH_MAX];
186 	uint32_t major = 0, minor = 0;
187 
188 	if (_control_fd != -1)
189 		return 1;
190 
191 	snprintf(control, sizeof(control), "%s/control", dm_dir());
192 
193 	if (!_control_device_number(&major, &minor))
194 		log_error("Is device-mapper driver missing from kernel?");
195 
196 	if (!_control_exists(control, major, minor) &&
197 	    !_create_control(control, major, minor))
198 		goto error;
199 
200 	if ((_control_fd = open(control, O_RDWR)) < 0) {
201 		log_sys_error("open", control);
202 		goto error;
203 	}
204 
205 	return 1;
206 
207 error:
208 	log_error("Failure to communicate with kernel device-mapper driver.");
209 	return 0;
210 }
211 
212 /*
213  * Destroy dm task structure there are some dynamically alocated values there.
214  * name, uuid, head, tail list.
215  */
216 void dm_task_destroy(struct dm_task *dmt)
217 {
218 	struct target *t, *n;
219 
220 	for (t = dmt->head; t; t = n) {
221 		n = t->next;
222 		dm_free(t->params);
223 		dm_free(t->type);
224 		dm_free(t);
225 	}
226 
227 	if (dmt->dev_name)
228 		dm_free(dmt->dev_name);
229 
230 	if (dmt->newname)
231 		dm_free(dmt->newname);
232 
233 	if (dmt->message)
234 		dm_free(dmt->message);
235 
236 	if (dmt->dmi.v4)
237 		dm_free(dmt->dmi.v4);
238 
239 	if (dmt->uuid)
240 		dm_free(dmt->uuid);
241 
242 	dm_free(dmt);
243 
244 }
245 
246 /* Get kernel driver version from dm_ioctl structure. */
247 int dm_task_get_driver_version(struct dm_task *dmt, char *version, size_t size)
248 {
249 	unsigned *v;
250 
251 	if (!dmt->dmi.v4) {
252 		version[0] = '\0';
253 		return 0;
254 	}
255 
256 	v = dmt->dmi.v4->version;
257 	snprintf(version, size, "%u.%u.%u", v[0], v[1], v[2]);
258 	_dm_version_minor = v[1];
259 	_dm_version_patchlevel = v[2];
260 
261 	return 1;
262 }
263 
264 /* Get kernel driver protocol version and comapre it with library version. */
265 static int _check_version(char *version, size_t size)
266 {
267 	struct dm_task *task;
268 	int r;
269 
270 	if (!(task = dm_task_create(DM_DEVICE_VERSION))) {
271 		log_error("Failed to get device-mapper version");
272 		version[0] = '\0';
273 		return 0;
274 	}
275 
276 	r = dm_task_run(task);
277 	dm_task_get_driver_version(task, version, size);
278 	dm_task_destroy(task);
279 
280 	return r;
281 }
282 
283 /*
284  * Find out device-mapper's major version number the first time
285  * this is called and whether or not we support it.
286  */
287 int dm_check_version(void)
288 {
289 	char dmversion[64];
290 
291 	if (_version_checked)
292 		return _version_ok;
293 
294 	_version_checked = 1;
295 
296 	if (_check_version(dmversion, sizeof(dmversion)))
297 		return 1;
298 
299 
300 	return 0;
301 }
302 
303 /* Get next target(table description) from list pointed by dmt->head. */
304 void *dm_get_next_target(struct dm_task *dmt, void *next,
305 			 uint64_t *start, uint64_t *length,
306 			 char **target_type, char **params)
307 {
308 	struct target *t = (struct target *) next;
309 
310 	if (!t)
311 		t = dmt->head;
312 
313 	if (!t)
314 		return NULL;
315 
316 	*start = t->start;
317 	*length = t->length;
318 	*target_type = t->type;
319 	*params = t->params;
320 
321 	return t->next;
322 }
323 
324 /* Unmarshall the target info returned from a status call */
325 static int _unmarshal_status(struct dm_task *dmt, struct dm_ioctl *dmi)
326 {
327 	char *outbuf = (char *) dmi + dmi->data_start;
328 	char *outptr = outbuf;
329 	uint32_t i;
330 	struct dm_target_spec *spec;
331 
332 	for (i = 0; i < dmi->target_count; i++) {
333 		spec = (struct dm_target_spec *) outptr;
334 		if (!dm_task_add_target(dmt, spec->sector_start,
335 					spec->length,
336 					spec->target_type,
337 					outptr + sizeof(*spec))) {
338 			return 0;
339 		}
340 
341 		outptr = outbuf + spec->next;
342 	}
343 
344 	return 1;
345 }
346 
347 /*
348  * @dev_major is major number of char device
349  *
350  * I have to find it's block device number and lookup dev in
351  * device database to find device path.
352  *
353  */
354 
355 int dm_format_dev(char *buf, int bufsize, uint32_t dev_major,
356 		  uint32_t dev_minor)
357 {
358 	int r;
359 	uint32_t major, dm_major;
360 	char *name;
361 	mode_t mode;
362 	dev_t dev;
363 	size_t val_len,i;
364 	struct kinfo_drivers *kd;
365 
366 	mode = 0;
367 
368 	nbsd_get_dm_major(&dm_major, DM_BLOCK_MAJOR);
369 
370 	log_error("format_dev %d--%d %d", dev_major, dev_minor, bufsize);
371 
372 	if (bufsize < 8)
373 		return 0;
374 
375 	if (sysctlbyname("kern.drivers",NULL,&val_len,NULL,0) < 0) {
376 		printf("sysctlbyname failed");
377 		return 0;
378 	}
379 
380 	if ((kd = malloc (val_len)) == NULL){
381 		printf("malloc kd info error\n");
382 		return 0;
383 	}
384 
385 	if (sysctlbyname("kern.drivers", kd, &val_len, NULL, 0) < 0) {
386 		printf("sysctlbyname failed kd");
387 		return 0;
388 	}
389 
390 	for (i = 0, val_len /= sizeof(*kd); i < val_len; i++){
391 		if (kd[i].d_cmajor == dev_major) {
392 			major = kd[i].d_bmajor;
393 			break;
394 		}
395 	}
396 
397 	dev = MKDEV(major,dev_minor);
398 
399 	mode |= S_IFBLK;
400 
401 	name = devname(dev,mode);
402 
403 	r = snprintf(buf, (size_t) bufsize, "/dev/%s",name);
404 
405 	free(kd);
406 
407 	if (r < 0 || r > bufsize - 1 || name == NULL)
408 		return 0;
409 
410 	return 1;
411 }
412 
413 /* Fill info from dm_ioctl structure. Look at DM_EXISTS_FLAG*/
414 int dm_task_get_info(struct dm_task *dmt, struct dm_info *info)
415 {
416 	if (!dmt->dmi.v4)
417 		return 0;
418 
419 	memset(info, 0, sizeof(*info));
420 
421 	info->exists = dmt->dmi.v4->flags & DM_EXISTS_FLAG ? 1 : 0;
422 	if (!info->exists)
423 		return 1;
424 
425 	info->suspended = dmt->dmi.v4->flags & DM_SUSPEND_FLAG ? 1 : 0;
426 	info->read_only = dmt->dmi.v4->flags & DM_READONLY_FLAG ? 1 : 0;
427 	info->live_table = dmt->dmi.v4->flags & DM_ACTIVE_PRESENT_FLAG ? 1 : 0;
428 	info->inactive_table = dmt->dmi.v4->flags & DM_INACTIVE_PRESENT_FLAG ?
429 	    1 : 0;
430 	info->target_count = dmt->dmi.v4->target_count;
431 	info->open_count = dmt->dmi.v4->open_count;
432 	info->event_nr = dmt->dmi.v4->event_nr;
433 
434 	nbsd_get_dm_major(&info->major, DM_BLOCK_MAJOR); /* get netbsd dm device major number */
435 	info->minor = MINOR(dmt->dmi.v4->dev);
436 
437 	return 1;
438 }
439 
440 /* Unsupported on NetBSD */
441 uint32_t dm_task_get_read_ahead(const struct dm_task *dmt, uint32_t *read_ahead)
442 {
443 	*read_ahead = DM_READ_AHEAD_NONE;
444 	return 1;
445 }
446 
447 const char *dm_task_get_name(const struct dm_task *dmt)
448 {
449 
450 	return (dmt->dmi.v4->name);
451 }
452 
453 const char *dm_task_get_uuid(const struct dm_task *dmt)
454 {
455 
456 	return (dmt->dmi.v4->uuid);
457 }
458 
459 struct dm_deps *dm_task_get_deps(struct dm_task *dmt)
460 {
461 	return (struct dm_deps *) (((void *) dmt->dmi.v4) +
462 				   dmt->dmi.v4->data_start);
463 }
464 
465 struct dm_names *dm_task_get_names(struct dm_task *dmt)
466 {
467 	return (struct dm_names *) (((void *) dmt->dmi.v4) +
468 				    dmt->dmi.v4->data_start);
469 }
470 
471 struct dm_versions *dm_task_get_versions(struct dm_task *dmt)
472 {
473 	return (struct dm_versions *) (((void *) dmt->dmi.v4) +
474 				       dmt->dmi.v4->data_start);
475 }
476 
477 int dm_task_set_ro(struct dm_task *dmt)
478 {
479 	dmt->read_only = 1;
480 	return 1;
481 }
482 
483 /* Unsupported on NetBSD */
484 int dm_task_set_read_ahead(struct dm_task *dmt, uint32_t read_ahead,
485 			   uint32_t read_ahead_flags)
486 {
487 	return 1;
488 }
489 
490 int dm_task_suppress_identical_reload(struct dm_task *dmt)
491 {
492 	dmt->suppress_identical_reload = 1;
493 	return 1;
494 }
495 
496 int dm_task_set_newname(struct dm_task *dmt, const char *newname)
497 {
498 	if (!(dmt->newname = dm_strdup(newname))) {
499 		log_error("dm_task_set_newname: strdup(%s) failed", newname);
500 		return 0;
501 	}
502 
503 	return 1;
504 }
505 
506 int dm_task_set_message(struct dm_task *dmt, const char *message)
507 {
508 	if (!(dmt->message = dm_strdup(message))) {
509 		log_error("dm_task_set_message: strdup(%s) failed", message);
510 		return 0;
511 	}
512 
513 	return 1;
514 }
515 
516 int dm_task_set_sector(struct dm_task *dmt, uint64_t sector)
517 {
518 	dmt->sector = sector;
519 
520 	return 1;
521 }
522 
523 /* Unsupported in NetBSD */
524 int dm_task_set_geometry(struct dm_task *dmt, const char *cylinders,
525     const char *heads, const char *sectors, const char *start)
526 {
527 	return 0;
528 }
529 
530 int dm_task_no_flush(struct dm_task *dmt)
531 {
532 	dmt->no_flush = 1;
533 
534 	return 1;
535 }
536 
537 int dm_task_no_open_count(struct dm_task *dmt)
538 {
539 	dmt->no_open_count = 1;
540 
541 	return 1;
542 }
543 
544 int dm_task_skip_lockfs(struct dm_task *dmt)
545 {
546 	dmt->skip_lockfs = 1;
547 
548 	return 1;
549 }
550 
551 int dm_task_set_event_nr(struct dm_task *dmt, uint32_t event_nr)
552 {
553 	dmt->event_nr = event_nr;
554 
555 	return 1;
556 }
557 
558 /* Allocate one target(table description) entry. */
559 struct target *create_target(uint64_t start, uint64_t len, const char *type,
560 			     const char *params)
561 {
562 	struct target *t = dm_malloc(sizeof(*t));
563 
564 	if (!t) {
565 		log_error("create_target: malloc(%" PRIsize_t ") failed",
566 			  sizeof(*t));
567 		return NULL;
568 	}
569 
570 	memset(t, 0, sizeof(*t));
571 
572 	if (!(t->params = dm_strdup(params))) {
573 		log_error("create_target: strdup(params) failed");
574 		goto bad;
575 	}
576 
577 	if (!(t->type = dm_strdup(type))) {
578 		log_error("create_target: strdup(type) failed");
579 		goto bad;
580 	}
581 
582 	t->start = start;
583 	t->length = len;
584 	return t;
585 
586       bad:
587 	dm_free(t->params);
588 	dm_free(t->type);
589 	dm_free(t);
590 	return NULL;
591 }
592 
593 /* Parse given dm task structure to proplib dictionary.  */
594 static int _flatten(struct dm_task *dmt, prop_dictionary_t dm_dict)
595 {
596 	prop_array_t cmd_array;
597 	prop_dictionary_t target_spec;
598 	prop_dictionary_t target_param;
599 
600 	struct target *t;
601 
602 	size_t len;
603 	char type[DM_MAX_TYPE_NAME];
604 
605 	uint32_t major, flags;
606 	int count = 0;
607 	const int (*version)[3];
608 
609 	flags = 0;
610 	version = &_cmd_data_v4[dmt->type].version;
611 
612 	cmd_array = prop_array_create();
613 
614 	for (t = dmt->head; t; t = t->next) {
615 		target_spec = prop_dictionary_create();
616 
617 		prop_dictionary_set_uint64(target_spec,DM_TABLE_START,t->start);
618 		prop_dictionary_set_uint64(target_spec,DM_TABLE_LENGTH,t->length);
619 
620 		strlcpy(type,t->type,DM_MAX_TYPE_NAME);
621 
622 		prop_dictionary_set_cstring(target_spec,DM_TABLE_TYPE,type);
623 
624 		target_param = nbsd_dm_parse_param(type, t->params);
625 		prop_dictionary_set(target_spec, DM_TABLE_PARAMS, target_param);
626 		prop_object_release(target_param);
627 
628 		prop_array_set(cmd_array,count,target_spec);
629 
630 		prop_object_release(target_spec);
631 
632 		count++;
633 	}
634 
635 
636 	if (count && (dmt->sector || dmt->message)) {
637 		log_error("targets and message are incompatible");
638 		return -1;
639 	}
640 
641 	if (count && dmt->newname) {
642 		log_error("targets and newname are incompatible");
643 		return -1;
644 	}
645 
646 	if (count && dmt->geometry) {
647 		log_error("targets and geometry are incompatible");
648 		return -1;
649 	}
650 
651 	if (dmt->newname && (dmt->sector || dmt->message)) {
652 		log_error("message and newname are incompatible");
653 		return -1;
654 	}
655 
656 	if (dmt->newname && dmt->geometry) {
657 		log_error("geometry and newname are incompatible");
658 		return -1;
659 	}
660 
661 	if (dmt->geometry && (dmt->sector || dmt->message)) {
662 		log_error("geometry and message are incompatible");
663 		return -1;
664 	}
665 
666 	if (dmt->sector && !dmt->message) {
667 		log_error("message is required with sector");
668 		return -1;
669 	}
670 
671 	if (dmt->newname)
672 		len += strlen(dmt->newname) + 1;
673 
674 	if (dmt->message)
675 		len += sizeof(struct dm_target_msg) + strlen(dmt->message) + 1;
676 
677 	if (dmt->geometry)
678 		len += strlen(dmt->geometry) + 1;
679 
680 	nbsd_dmi_add_version((*version), dm_dict);
681 
682 	nbsd_get_dm_major(&major, DM_BLOCK_MAJOR);
683 	/*
684 	 * Only devices with major which is equal to netbsd dm major
685 	 * dm devices in NetBSD can't have more majors then one assigned to dm.
686 	 */
687 	if (dmt->major != major && dmt->major != -1)
688 		return -1;
689 
690 	if (dmt->minor >= 0) {
691 		flags |= DM_PERSISTENT_DEV_FLAG;
692 
693 		prop_dictionary_set_uint32(dm_dict, DM_IOCTL_MINOR, dmt->minor);
694 	}
695 
696 	/* Set values to dictionary. */
697 	if (dmt->dev_name)
698 		prop_dictionary_set_cstring(dm_dict, DM_IOCTL_NAME, dmt->dev_name);
699 
700 	if (dmt->uuid)
701 		prop_dictionary_set_cstring(dm_dict, DM_IOCTL_UUID, dmt->uuid);
702 
703 	if (dmt->type == DM_DEVICE_SUSPEND)
704 		flags |= DM_SUSPEND_FLAG;
705 	if (dmt->no_flush)
706 		flags |= DM_NOFLUSH_FLAG;
707 	if (dmt->read_only)
708 		flags |= DM_READONLY_FLAG;
709 	if (dmt->skip_lockfs)
710 		flags |= DM_SKIP_LOCKFS_FLAG;
711 
712 	prop_dictionary_set_uint32(dm_dict, DM_IOCTL_FLAGS, flags);
713 
714 	prop_dictionary_set_uint32(dm_dict, DM_IOCTL_EVENT, dmt->event_nr);
715 
716 	if (dmt->newname)
717 		prop_array_set_cstring(cmd_array, 0, dmt->newname);
718 
719 	/* Add array for all COMMAND specific data. */
720 	prop_dictionary_set(dm_dict, DM_IOCTL_CMD_DATA, cmd_array);
721 	prop_object_release(cmd_array);
722 
723 	return 0;
724 }
725 
726 static int _process_mapper_dir(struct dm_task *dmt)
727 {
728 	struct dirent *dirent;
729 	DIR *d;
730 	const char *dir;
731 	int r = 1;
732 
733 	dir = dm_dir();
734 	if (!(d = opendir(dir))) {
735 		log_sys_error("opendir", dir);
736 		return 0;
737 	}
738 
739 	while ((dirent = readdir(d))) {
740 		if (!strcmp(dirent->d_name, ".") ||
741 		    !strcmp(dirent->d_name, "..") ||
742 		    !strcmp(dirent->d_name, "control"))
743 			continue;
744 		dm_task_set_name(dmt, dirent->d_name);
745 		dm_task_run(dmt);
746 	}
747 
748 	if (closedir(d))
749 		log_sys_error("closedir", dir);
750 
751 	return r;
752 }
753 
754 /* Get list of all devices. */
755 static int _process_all_v4(struct dm_task *dmt)
756 {
757 	struct dm_task *task;
758 	struct dm_names *names;
759 	unsigned next = 0;
760 	int r = 1;
761 
762 	if (!(task = dm_task_create(DM_DEVICE_LIST)))
763 		return 0;
764 
765 	if (!dm_task_run(task)) {
766 		r = 0;
767 		goto out;
768 	}
769 
770 	if (!(names = dm_task_get_names(task))) {
771 		r = 0;
772 		goto out;
773 	}
774 
775 	if (!names->dev)
776 		goto out;
777 
778 	do {
779 		names = (void *) names + next;
780 		if (!dm_task_set_name(dmt, names->name)) {
781 			r = 0;
782 			goto out;
783 		}
784 		if (!dm_task_run(dmt))
785 			r = 0;
786 		next = names->next;
787 	} while (next);
788 
789       out:
790 	dm_task_destroy(task);
791 	return r;
792 }
793 
794 static int _mknodes_v4(struct dm_task *dmt)
795 {
796 	(void) _process_mapper_dir(dmt);
797 
798 	return _process_all_v4(dmt);
799 }
800 
801 /* Create new device and load table to it. */
802 static int _create_and_load_v4(struct dm_task *dmt)
803 {
804 	struct dm_task *task;
805 	int r;
806 
807 	printf("create and load called \n");
808 
809 	/* Use new task struct to create the device */
810 	if (!(task = dm_task_create(DM_DEVICE_CREATE))) {
811 		log_error("Failed to create device-mapper task struct");
812 		return 0;
813 	}
814 
815 	/* Copy across relevant fields */
816 	if (dmt->dev_name && !dm_task_set_name(task, dmt->dev_name)) {
817 		dm_task_destroy(task);
818 		return 0;
819 	}
820 
821 	if (dmt->uuid && !dm_task_set_uuid(task, dmt->uuid)) {
822 		dm_task_destroy(task);
823 		return 0;
824 	}
825 
826 	task->major = dmt->major;
827 	task->minor = dmt->minor;
828 	task->uid = dmt->uid;
829 	task->gid = dmt->gid;
830 	task->mode = dmt->mode;
831 
832 	r = dm_task_run(task);
833 	dm_task_destroy(task);
834 	if (!r)
835 		return r;
836 
837 	/* Next load the table */
838 	if (!(task = dm_task_create(DM_DEVICE_RELOAD))) {
839 		log_error("Failed to create device-mapper task struct");
840 		return 0;
841 	}
842 
843 	/* Copy across relevant fields */
844 	if (dmt->dev_name && !dm_task_set_name(task, dmt->dev_name)) {
845 		dm_task_destroy(task);
846 		return 0;
847 	}
848 
849 	task->read_only = dmt->read_only;
850 	task->head = dmt->head;
851 	task->tail = dmt->tail;
852 
853 	r = dm_task_run(task);
854 
855 	task->head = NULL;
856 	task->tail = NULL;
857 	dm_task_destroy(task);
858 	if (!r)
859 		goto revert;
860 
861 	/* Use the original structure last so the info will be correct */
862 	dmt->type = DM_DEVICE_RESUME;
863 	dm_free(dmt->uuid);
864 	dmt->uuid = NULL;
865 
866 	r = dm_task_run(dmt);
867 
868 	if (r)
869 		return r;
870 
871       revert:
872  	dmt->type = DM_DEVICE_REMOVE;
873 	dm_free(dmt->uuid);
874 	dmt->uuid = NULL;
875 
876 	if (!dm_task_run(dmt))
877 		log_error("Failed to revert device creation.");
878 
879 	return r;
880 }
881 
882 uint64_t dm_task_get_existing_table_size(struct dm_task *dmt)
883 {
884 	return dmt->existing_table_size;
885 }
886 
887 static int _reload_with_suppression_v4(struct dm_task *dmt)
888 {
889 	struct dm_task *task;
890 	struct target *t1, *t2;
891 	int r;
892 
893 	/* New task to get existing table information */
894 	if (!(task = dm_task_create(DM_DEVICE_TABLE))) {
895 		log_error("Failed to create device-mapper task struct");
896 		return 0;
897 	}
898 
899 	/* Copy across relevant fields */
900 	if (dmt->dev_name && !dm_task_set_name(task, dmt->dev_name)) {
901 		dm_task_destroy(task);
902 		return 0;
903 	}
904 
905 	if (dmt->uuid && !dm_task_set_uuid(task, dmt->uuid)) {
906 		dm_task_destroy(task);
907 		return 0;
908 	}
909 
910 	task->major = dmt->major;
911 	task->minor = dmt->minor;
912 
913 	r = dm_task_run(task);
914 
915 	if (!r) {
916 		dm_task_destroy(task);
917 		return r;
918 	}
919 
920 	/* Store existing table size */
921 	t2 = task->head;
922 	while (t2 && t2->next)
923 		t2 = t2->next;
924 	dmt->existing_table_size = t2 ? t2->start + t2->length : 0;
925 
926 	if ((task->dmi.v4->flags & DM_READONLY_FLAG) ? 1 : 0 != dmt->read_only)
927 		goto no_match;
928 
929 	t1 = dmt->head;
930 	t2 = task->head;
931 
932 	while (t1 && t2) {
933 		while (t2->params[strlen(t2->params) - 1] == ' ')
934 			t2->params[strlen(t2->params) - 1] = '\0';
935 		if ((t1->start != t2->start) ||
936 		    (t1->length != t2->length) ||
937 		    (strcmp(t1->type, t2->type)) ||
938 		    (strcmp(t1->params, t2->params)))
939 			goto no_match;
940 		t1 = t1->next;
941 		t2 = t2->next;
942 	}
943 
944 	if (!t1 && !t2) {
945 		dmt->dmi.v4 = task->dmi.v4;
946 		task->dmi.v4 = NULL;
947 		dm_task_destroy(task);
948 		return 1;
949 	}
950 
951 no_match:
952 	dm_task_destroy(task);
953 
954 	/* Now do the original reload */
955 	dmt->suppress_identical_reload = 0;
956 	r = dm_task_run(dmt);
957 
958 	return r;
959 }
960 
961 /*
962  * This function is heart of NetBSD libdevmapper-> device-mapper kernel protocol
963  * It creates proplib_dictionary from dm task structure and sends it to NetBSD
964  * kernel driver. After succesfull ioctl it create dmi structure from returned
965  * proplib dictionary. This way I keep number of changes in NetBSD version of
966  * libdevmapper as small as posible.
967  */
968 static struct dm_ioctl *_do_dm_ioctl(struct dm_task *dmt, unsigned command)
969 {
970 	struct dm_ioctl *dmi;
971 	prop_dictionary_t dm_dict_in, dm_dict_out;
972 
973 	uint32_t flags;
974 
975 	dm_dict_in = NULL;
976 
977 	dm_dict_in = prop_dictionary_create(); /* Dictionary send to kernel */
978 	dm_dict_out = prop_dictionary_create(); /* Dictionary received from kernel */
979 
980 	/* Set command name to dictionary */
981 	prop_dictionary_set_cstring(dm_dict_in, DM_IOCTL_COMMAND,
982 	    _cmd_data_v4[dmt->type].name);
983 
984 	/* Parse dmi from libdevmapper to dictionary */
985 	if (_flatten(dmt, dm_dict_in) < 0)
986 		goto bad;
987 
988 	prop_dictionary_get_uint32(dm_dict_in, DM_IOCTL_FLAGS, &flags);
989 
990 	if (dmt->type == DM_DEVICE_TABLE)
991 		flags |= DM_STATUS_TABLE_FLAG;
992 
993 	if (dmt->no_open_count)
994 		flags |= DM_SKIP_BDGET_FLAG;
995 
996 	flags |= DM_EXISTS_FLAG;
997 
998 	/* Set flags to dictionary. */
999 	prop_dictionary_set_uint32(dm_dict_in,DM_IOCTL_FLAGS,flags);
1000 
1001 	prop_dictionary_externalize_to_file(dm_dict_in,"/tmp/test_in");
1002 
1003 	log_very_verbose("Ioctl type  %s --- flags %d",_cmd_data_v4[dmt->type].name,flags);
1004 	//printf("name %s, major %d minor %d\n uuid %s\n",
1005         //dm_task_get_name(dmt), dmt->minor, dmt->major, dm_task_get_uuid(dmt));
1006 	/* Send dictionary to kernel and wait for reply. */
1007 	if (prop_dictionary_sendrecv_ioctl(dm_dict_in,_control_fd,
1008 		NETBSD_DM_IOCTL,&dm_dict_out) != 0) {
1009 
1010 		if (errno == ENOENT &&
1011 		    ((dmt->type == DM_DEVICE_INFO) ||
1012 			(dmt->type == DM_DEVICE_MKNODES) ||
1013 			(dmt->type == DM_DEVICE_STATUS))) {
1014 
1015 			/*
1016 			 * Linux version doesn't fail when ENOENT is returned
1017 			 * for nonexisting device after info, deps, mknodes call.
1018 			 * It returns dmi sent to kernel with DM_EXISTS_FLAG = 0;
1019 			 */
1020 
1021 			dmi = nbsd_dm_dict_to_dmi(dm_dict_in,_cmd_data_v4[dmt->type].cmd);
1022 
1023 			dmi->flags &= ~DM_EXISTS_FLAG;
1024 
1025 			prop_object_release(dm_dict_in);
1026 			prop_object_release(dm_dict_out);
1027 
1028 			goto out;
1029 		} else {
1030 			log_error("ioctl %s call failed with errno %d\n",
1031 					  _cmd_data_v4[dmt->type].name, errno);
1032 
1033 			prop_object_release(dm_dict_in);
1034 			prop_object_release(dm_dict_out);
1035 
1036 			goto bad;
1037 		}
1038 	}
1039 
1040 	prop_dictionary_externalize_to_file(dm_dict_out,"/tmp/test_out");
1041 
1042 	/* Parse kernel dictionary to dmi structure and return it to libdevmapper. */
1043 	dmi = nbsd_dm_dict_to_dmi(dm_dict_out,_cmd_data_v4[dmt->type].cmd);
1044 
1045 	prop_object_release(dm_dict_in);
1046 	prop_object_release(dm_dict_out);
1047 out:
1048 	return dmi;
1049 bad:
1050 	return NULL;
1051 }
1052 
1053 /* Create new edvice nodes in mapper/ dir. */
1054 void dm_task_update_nodes(void)
1055 {
1056 	update_devs();
1057 }
1058 
1059 /* Run dm command which is descirbed in dm_task structure. */
1060 int dm_task_run(struct dm_task *dmt)
1061 {
1062 	struct dm_ioctl *dmi;
1063 	unsigned command;
1064 
1065 	if ((unsigned) dmt->type >=
1066 	    (sizeof(_cmd_data_v4) / sizeof(*_cmd_data_v4))) {
1067 		log_error("Internal error: unknown device-mapper task %d",
1068 			  dmt->type);
1069 		return 0;
1070 	}
1071 
1072 	command = _cmd_data_v4[dmt->type].cmd;
1073 
1074 	/* Old-style creation had a table supplied */
1075 	if (dmt->type == DM_DEVICE_CREATE && dmt->head)
1076 		return _create_and_load_v4(dmt);
1077 
1078 	if (dmt->type == DM_DEVICE_MKNODES && !dmt->dev_name &&
1079 	    !dmt->uuid && dmt->major <= 0)
1080 		return _mknodes_v4(dmt);
1081 
1082 	if ((dmt->type == DM_DEVICE_RELOAD) && dmt->suppress_identical_reload)
1083 		return _reload_with_suppression_v4(dmt);
1084 
1085 	if (!_open_control())
1086 		return 0;
1087 
1088 	if (!(dmi = _do_dm_ioctl(dmt, command)))
1089 		return 0;
1090 
1091 	switch (dmt->type) {
1092 	case DM_DEVICE_CREATE:
1093 		add_dev_node(dmt->dev_name, MAJOR(dmi->dev), MINOR(dmi->dev),
1094 			     dmt->uid, dmt->gid, dmt->mode);
1095 		break;
1096 
1097 	case DM_DEVICE_REMOVE:
1098 		/* FIXME Kernel needs to fill in dmi->name */
1099 		if (dmt->dev_name)
1100 			rm_dev_node(dmt->dev_name);
1101 		break;
1102 
1103 	case DM_DEVICE_RENAME:
1104 		/* FIXME Kernel needs to fill in dmi->name */
1105 		if (dmt->dev_name)
1106 			rename_dev_node(dmt->dev_name, dmt->newname);
1107 		break;
1108 
1109 	case DM_DEVICE_RESUME:
1110 		/* FIXME Kernel needs to fill in dmi->name */
1111 		set_dev_node_read_ahead(dmt->dev_name, dmt->read_ahead,
1112 					dmt->read_ahead_flags);
1113 		break;
1114 
1115 	case DM_DEVICE_MKNODES:
1116 		if (dmi->flags & DM_EXISTS_FLAG)
1117 			add_dev_node(dmi->name, MAJOR(dmi->dev),
1118 				     MINOR(dmi->dev),
1119 				     dmt->uid, dmt->gid, dmt->mode);
1120 		else if (dmt->dev_name)
1121 			rm_dev_node(dmt->dev_name);
1122 		break;
1123 
1124 	case DM_DEVICE_STATUS:
1125 	case DM_DEVICE_TABLE:
1126 	case DM_DEVICE_WAITEVENT:
1127 		if (!_unmarshal_status(dmt, dmi))
1128 			goto bad;
1129 		break;
1130 	}
1131 
1132 	/* Was structure reused? */
1133 	if (dmt->dmi.v4)
1134 		dm_free(dmt->dmi.v4);
1135 
1136 	dmt->dmi.v4 = dmi;
1137 	return 1;
1138 
1139       bad:
1140 	dm_free(dmi);
1141 	return 0;
1142 }
1143 
1144 void dm_lib_release(void)
1145 {
1146 	if (_control_fd != -1) {
1147 		close(_control_fd);
1148 		_control_fd = -1;
1149 	}
1150 	update_devs();
1151 }
1152 
1153 void dm_lib_exit(void)
1154 {
1155 	dm_lib_release();
1156 	dm_dump_memory();
1157 	_version_ok = 1;
1158 	_version_checked = 0;
1159 }
1160