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