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