1 /* $NetBSD: dm_ioctl.c,v 1.13 2009/06/05 21:52:31 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 dmv->diskp->dk_info = NULL; 252 253 if ((r = dm_dev_insert(dmv)) != 0) 254 dm_dev_free(dmv); 255 256 DM_ADD_FLAG(flags, DM_EXISTS_FLAG); 257 DM_REMOVE_FLAG(flags, DM_INACTIVE_PRESENT_FLAG); 258 259 /* Increment device counter After creating device */ 260 atomic_inc_64(&dev_counter); 261 262 return r; 263 } 264 265 /* 266 * Get list of created device-mapper devices fromglobal list and 267 * send it to kernel. 268 * 269 * Output dictionary: 270 * 271 * <key>cmd_data</key> 272 * <array> 273 * <dict> 274 * <key>name<key> 275 * <string>...</string> 276 * 277 * <key>dev</key> 278 * <integer>...</integer> 279 * </dict> 280 * </array> 281 * 282 */ 283 int 284 dm_dev_list_ioctl(prop_dictionary_t dm_dict) 285 { 286 prop_array_t dev_list; 287 288 uint32_t flags; 289 290 flags = 0; 291 292 prop_dictionary_get_uint32(dm_dict, DM_IOCTL_FLAGS, &flags); 293 294 dm_dbg_print_flags(flags); 295 296 dev_list = dm_dev_prop_list(); 297 298 prop_dictionary_set(dm_dict, DM_IOCTL_CMD_DATA, dev_list); 299 prop_object_release(dev_list); 300 301 return 0; 302 } 303 304 /* 305 * Rename selected devices old name is in struct dm_ioctl. 306 * newname is taken from dictionary 307 * 308 * <key>cmd_data</key> 309 * <array> 310 * <string>...</string> 311 * </array> 312 */ 313 int 314 dm_dev_rename_ioctl(prop_dictionary_t dm_dict) 315 { 316 prop_array_t cmd_array; 317 dm_dev_t *dmv; 318 319 const char *name, *uuid, *n_name; 320 uint32_t flags, minor; 321 322 name = NULL; 323 uuid = NULL; 324 minor = 0; 325 326 /* Get needed values from dictionary. */ 327 prop_dictionary_get_cstring_nocopy(dm_dict, DM_IOCTL_NAME, &name); 328 prop_dictionary_get_cstring_nocopy(dm_dict, DM_IOCTL_UUID, &uuid); 329 prop_dictionary_get_uint32(dm_dict, DM_IOCTL_FLAGS, &flags); 330 prop_dictionary_get_uint32(dm_dict, DM_IOCTL_MINOR, &minor); 331 332 dm_dbg_print_flags(flags); 333 334 cmd_array = prop_dictionary_get(dm_dict, DM_IOCTL_CMD_DATA); 335 336 prop_array_get_cstring_nocopy(cmd_array, 0, &n_name); 337 338 if (strlen(n_name) + 1 > DM_NAME_LEN) 339 return EINVAL; 340 341 if ((dmv = dm_dev_rem(name, uuid, minor)) == NULL) { 342 DM_REMOVE_FLAG(flags, DM_EXISTS_FLAG); 343 return ENOENT; 344 } 345 346 /* change device name */ 347 /* XXX How to deal with this change, name only used in 348 * dm_dev_routines, should I add dm_dev_change_name which will run under the 349 * dm_dev_list mutex ? 350 */ 351 strlcpy(dmv->name, n_name, DM_NAME_LEN); 352 353 prop_dictionary_set_uint32(dm_dict, DM_IOCTL_OPEN, dmv->table_head.io_cnt); 354 prop_dictionary_set_uint32(dm_dict, DM_IOCTL_MINOR, dmv->minor); 355 prop_dictionary_set_cstring(dm_dict, DM_IOCTL_UUID, dmv->uuid); 356 357 dm_dev_insert(dmv); 358 359 return 0; 360 } 361 362 /* 363 * Remove device from global list I have to remove active 364 * and inactive tables first. 365 */ 366 int 367 dm_dev_remove_ioctl(prop_dictionary_t dm_dict) 368 { 369 dm_dev_t *dmv; 370 const char *name, *uuid; 371 uint32_t flags, minor; 372 373 flags = 0; 374 name = NULL; 375 uuid = NULL; 376 377 /* Get needed values from dictionary. */ 378 prop_dictionary_get_cstring_nocopy(dm_dict, DM_IOCTL_NAME, &name); 379 prop_dictionary_get_cstring_nocopy(dm_dict, DM_IOCTL_UUID, &uuid); 380 prop_dictionary_get_uint32(dm_dict, DM_IOCTL_FLAGS, &flags); 381 prop_dictionary_get_uint32(dm_dict, DM_IOCTL_MINOR, &minor); 382 383 dm_dbg_print_flags(flags); 384 385 /* Remove device from global device list */ 386 if ((dmv = dm_dev_rem(name, uuid, minor)) == NULL){ 387 DM_REMOVE_FLAG(flags, DM_EXISTS_FLAG); 388 return ENOENT; 389 } 390 391 /* Destroy active table first. */ 392 dm_table_destroy(&dmv->table_head, DM_TABLE_ACTIVE); 393 394 /* Destroy inactive table if exits, too. */ 395 dm_table_destroy(&dmv->table_head, DM_TABLE_INACTIVE); 396 397 dm_table_head_destroy(&dmv->table_head); 398 399 /* Destroy disk device structure */ 400 disk_detach(dmv->diskp); 401 disk_destroy(dmv->diskp); 402 403 /* Destroy device */ 404 (void)dm_dev_free(dmv); 405 406 /* Decrement device counter After removing device */ 407 atomic_dec_64(&dev_counter); 408 409 return 0; 410 } 411 412 /* 413 * Return actual state of device to libdevmapper. 414 */ 415 int 416 dm_dev_status_ioctl(prop_dictionary_t dm_dict) 417 { 418 dm_dev_t *dmv; 419 const char *name, *uuid; 420 uint32_t flags, j, minor; 421 422 name = NULL; 423 uuid = NULL; 424 flags = 0; 425 j = 0; 426 427 prop_dictionary_get_cstring_nocopy(dm_dict, DM_IOCTL_NAME, &name); 428 prop_dictionary_get_cstring_nocopy(dm_dict, DM_IOCTL_UUID, &uuid); 429 prop_dictionary_get_uint32(dm_dict, DM_IOCTL_FLAGS, &flags); 430 prop_dictionary_get_uint32(dm_dict, DM_IOCTL_MINOR, &minor); 431 432 if ((dmv = dm_dev_lookup(name, uuid, minor)) == NULL) { 433 DM_REMOVE_FLAG(flags, DM_EXISTS_FLAG); 434 return ENOENT; 435 } 436 437 dm_dbg_print_flags(dmv->flags); 438 439 prop_dictionary_set_uint32(dm_dict, DM_IOCTL_OPEN, dmv->table_head.io_cnt); 440 prop_dictionary_set_uint32(dm_dict, DM_IOCTL_MINOR, dmv->minor); 441 prop_dictionary_set_cstring(dm_dict, DM_IOCTL_UUID, dmv->uuid); 442 443 if (dmv->flags & DM_SUSPEND_FLAG) 444 DM_ADD_FLAG(flags, DM_SUSPEND_FLAG); 445 446 /* Add status flags for tables I have to check both 447 active and inactive tables. */ 448 if ((j = dm_table_get_target_count(&dmv->table_head, DM_TABLE_ACTIVE))) { 449 DM_ADD_FLAG(flags, DM_ACTIVE_PRESENT_FLAG); 450 } else 451 DM_REMOVE_FLAG(flags, DM_ACTIVE_PRESENT_FLAG); 452 453 prop_dictionary_set_uint32(dm_dict, DM_IOCTL_TARGET_COUNT, j); 454 455 if (dm_table_get_target_count(&dmv->table_head, DM_TABLE_INACTIVE)) 456 DM_ADD_FLAG(flags, DM_INACTIVE_PRESENT_FLAG); 457 else 458 DM_REMOVE_FLAG(flags, DM_INACTIVE_PRESENT_FLAG); 459 460 dm_dev_unbusy(dmv); 461 462 return 0; 463 } 464 465 /* 466 * Set only flag to suggest that device is suspended. This call is 467 * not supported in NetBSD. 468 * 469 */ 470 int 471 dm_dev_suspend_ioctl(prop_dictionary_t dm_dict) 472 { 473 dm_dev_t *dmv; 474 const char *name, *uuid; 475 uint32_t flags, minor; 476 477 name = NULL; 478 uuid = NULL; 479 flags = 0; 480 481 prop_dictionary_get_cstring_nocopy(dm_dict, DM_IOCTL_NAME, &name); 482 prop_dictionary_get_cstring_nocopy(dm_dict, DM_IOCTL_UUID, &uuid); 483 prop_dictionary_get_uint32(dm_dict, DM_IOCTL_FLAGS, &flags); 484 prop_dictionary_get_uint32(dm_dict, DM_IOCTL_MINOR, &minor); 485 486 if ((dmv = dm_dev_lookup(name, uuid, minor)) == NULL){ 487 DM_REMOVE_FLAG(flags, DM_EXISTS_FLAG); 488 return ENOENT; 489 } 490 491 atomic_or_32(&dmv->flags, DM_SUSPEND_FLAG); 492 493 dm_dbg_print_flags(dmv->flags); 494 495 prop_dictionary_set_uint32(dm_dict, DM_IOCTL_OPEN, dmv->table_head.io_cnt); 496 prop_dictionary_set_uint32(dm_dict, DM_IOCTL_FLAGS, dmv->flags); 497 prop_dictionary_set_uint32(dm_dict, DM_IOCTL_MINOR, dmv->minor); 498 499 dm_dev_unbusy(dmv); 500 501 /* Add flags to dictionary flag after dmv -> dict copy */ 502 DM_ADD_FLAG(flags, DM_EXISTS_FLAG); 503 504 return 0; 505 } 506 507 /* 508 * Simulate Linux behaviour better and switch tables here and not in 509 * dm_table_load_ioctl. 510 */ 511 int 512 dm_dev_resume_ioctl(prop_dictionary_t dm_dict) 513 { 514 dm_dev_t *dmv; 515 const char *name, *uuid; 516 uint32_t flags, minor; 517 518 name = NULL; 519 uuid = NULL; 520 flags = 0; 521 522 /* char *xml; 523 xml = prop_dictionary_externalize(dm_dict); 524 printf("%s\n",xml);*/ 525 526 prop_dictionary_get_cstring_nocopy(dm_dict, DM_IOCTL_NAME, &name); 527 prop_dictionary_get_cstring_nocopy(dm_dict, DM_IOCTL_UUID, &uuid); 528 prop_dictionary_get_uint32(dm_dict, DM_IOCTL_FLAGS, &flags); 529 prop_dictionary_get_uint32(dm_dict, DM_IOCTL_MINOR, &minor); 530 531 /* Remove device from global device list */ 532 if ((dmv = dm_dev_lookup(name, uuid, minor)) == NULL){ 533 DM_REMOVE_FLAG(flags, DM_EXISTS_FLAG); 534 return ENOENT; 535 } 536 537 atomic_and_32(&dmv->flags, ~(DM_SUSPEND_FLAG | DM_INACTIVE_PRESENT_FLAG)); 538 atomic_or_32(&dmv->flags, DM_ACTIVE_PRESENT_FLAG); 539 540 dm_table_switch_tables(&dmv->table_head); 541 542 DM_ADD_FLAG(flags, DM_EXISTS_FLAG); 543 544 dmgetproperties(dmv->diskp, &dmv->table_head); 545 546 prop_dictionary_set_uint32(dm_dict, DM_IOCTL_OPEN, dmv->table_head.io_cnt); 547 prop_dictionary_set_uint32(dm_dict, DM_IOCTL_FLAGS, dmv->flags); 548 prop_dictionary_set_uint32(dm_dict, DM_IOCTL_MINOR, dmv->minor); 549 550 dm_dev_unbusy(dmv); 551 552 /* Destroy inactive table after resume. */ 553 dm_table_destroy(&dmv->table_head, DM_TABLE_INACTIVE); 554 555 return 0; 556 } 557 558 /* 559 * Table management routines 560 * lvm2tools doens't send name/uuid to kernel with table 561 * for lookup I have to use minor number. 562 */ 563 564 /* 565 * Remove inactive table from device. Routines which work's with inactive tables 566 * doesn't need to synchronise with dmstrategy. They can synchronise themselves with mutex?. 567 * 568 */ 569 int 570 dm_table_clear_ioctl(prop_dictionary_t dm_dict) 571 { 572 dm_dev_t *dmv; 573 const char *name, *uuid; 574 uint32_t flags, minor; 575 576 dmv = NULL; 577 name = NULL; 578 uuid = NULL; 579 flags = 0; 580 minor = 0; 581 582 prop_dictionary_get_cstring_nocopy(dm_dict, DM_IOCTL_NAME, &name); 583 prop_dictionary_get_cstring_nocopy(dm_dict, DM_IOCTL_UUID, &uuid); 584 prop_dictionary_get_uint32(dm_dict, DM_IOCTL_FLAGS, &flags); 585 prop_dictionary_get_uint32(dm_dict, DM_IOCTL_MINOR, &minor); 586 587 aprint_debug("Clearing inactive table from device: %s--%s\n", 588 name, uuid); 589 590 if ((dmv = dm_dev_lookup(name, uuid, minor)) == NULL){ 591 DM_REMOVE_FLAG(flags, DM_EXISTS_FLAG); 592 return ENOENT; 593 } 594 595 /* Select unused table */ 596 dm_table_destroy(&dmv->table_head, DM_TABLE_INACTIVE); 597 598 atomic_and_32(&dmv->flags, ~DM_INACTIVE_PRESENT_FLAG); 599 600 dm_dev_unbusy(dmv); 601 602 return 0; 603 } 604 605 /* 606 * Get list of physical devices for active table. 607 * Get dev_t from pdev vnode and insert it into cmd_array. 608 * 609 * XXX. This function is called from lvm2tools to get information 610 * about physical devices, too e.g. during vgcreate. 611 */ 612 int 613 dm_table_deps_ioctl(prop_dictionary_t dm_dict) 614 { 615 dm_dev_t *dmv; 616 dm_table_t *tbl; 617 dm_table_entry_t *table_en; 618 619 prop_array_t cmd_array; 620 const char *name, *uuid; 621 uint32_t flags, minor; 622 623 size_t i; 624 625 name = NULL; 626 uuid = NULL; 627 dmv = NULL; 628 flags = 0; 629 630 i = 0; 631 632 prop_dictionary_get_cstring_nocopy(dm_dict, DM_IOCTL_NAME, &name); 633 prop_dictionary_get_cstring_nocopy(dm_dict, DM_IOCTL_UUID, &uuid); 634 prop_dictionary_get_uint32(dm_dict, DM_IOCTL_FLAGS, &flags); 635 prop_dictionary_get_uint32(dm_dict, DM_IOCTL_MINOR, &minor); 636 637 /* create array for dev_t's */ 638 cmd_array = prop_array_create(); 639 640 if ((dmv = dm_dev_lookup(name, uuid, minor)) == NULL){ 641 DM_REMOVE_FLAG(flags, DM_EXISTS_FLAG); 642 return ENOENT; 643 } 644 645 prop_dictionary_set_uint32(dm_dict, DM_IOCTL_MINOR, dmv->minor); 646 prop_dictionary_set_cstring(dm_dict, DM_IOCTL_NAME, dmv->name); 647 prop_dictionary_set_cstring(dm_dict, DM_IOCTL_UUID, dmv->uuid); 648 649 aprint_debug("Getting table deps for device: %s\n", dmv->name); 650 651 tbl = dm_table_get_entry(&dmv->table_head, DM_TABLE_ACTIVE); 652 653 SLIST_FOREACH(table_en, tbl, next) 654 table_en->target->deps(table_en, cmd_array); 655 656 dm_table_release(&dmv->table_head, DM_TABLE_ACTIVE); 657 dm_dev_unbusy(dmv); 658 659 prop_dictionary_set(dm_dict, DM_IOCTL_CMD_DATA, cmd_array); 660 prop_object_release(cmd_array); 661 662 return 0; 663 } 664 665 /* 666 * Load new table/tables to device. 667 * Call apropriate target init routine open all physical pdev's and 668 * link them to device. For other targets mirror, strip, snapshot 669 * etc. also add dependency devices to upcalls list. 670 * 671 * Load table to inactive slot table are switched in dm_device_resume_ioctl. 672 * This simulates Linux behaviour better there should not be any difference. 673 * 674 */ 675 int 676 dm_table_load_ioctl(prop_dictionary_t dm_dict) 677 { 678 dm_dev_t *dmv; 679 dm_table_entry_t *table_en, *last_table; 680 dm_table_t *tbl; 681 dm_target_t *target; 682 683 prop_object_iterator_t iter; 684 prop_array_t cmd_array; 685 prop_dictionary_t target_dict, param_dict; 686 687 const char *name, *uuid, *type; 688 689 uint32_t flags, ret, minor; 690 691 ret = 0; 692 flags = 0; 693 name = NULL; 694 uuid = NULL; 695 dmv = NULL; 696 last_table = NULL; 697 698 /* char *xml; 699 xml = prop_dictionary_externalize(dm_dict); 700 printf("%s\n",xml);*/ 701 702 prop_dictionary_get_cstring_nocopy(dm_dict, DM_IOCTL_NAME, &name); 703 prop_dictionary_get_cstring_nocopy(dm_dict, DM_IOCTL_UUID, &uuid); 704 prop_dictionary_get_uint32(dm_dict, DM_IOCTL_FLAGS, &flags); 705 prop_dictionary_get_uint32(dm_dict, DM_IOCTL_MINOR, &minor); 706 707 cmd_array = prop_dictionary_get(dm_dict, DM_IOCTL_CMD_DATA); 708 iter = prop_array_iterator(cmd_array); 709 dm_dbg_print_flags(flags); 710 711 if ((dmv = dm_dev_lookup(name, uuid, minor)) == NULL){ 712 DM_REMOVE_FLAG(flags, DM_EXISTS_FLAG); 713 return ENOENT; 714 } 715 716 aprint_debug("Loading table to device: %s--%d\n", name, 717 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 aprint_debug("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 * If we want to deny table with 2 or more different 739 * target we should do it here 740 */ 741 if (((target = dm_target_lookup(type)) == NULL) && 742 ((target = dm_target_autoload(type)) == NULL)) { 743 dm_table_release(&dmv->table_head, DM_TABLE_INACTIVE); 744 dm_dev_unbusy(dmv); 745 return ENOENT; 746 } 747 748 if ((table_en = kmem_alloc(sizeof(dm_table_entry_t), 749 KM_NOSLEEP)) == NULL) { 750 dm_table_release(&dmv->table_head, DM_TABLE_INACTIVE); 751 dm_dev_unbusy(dmv); 752 return ENOMEM; 753 } 754 755 prop_dictionary_get_uint64(target_dict, DM_TABLE_START, 756 &table_en->start); 757 prop_dictionary_get_uint64(target_dict, DM_TABLE_LENGTH, 758 &table_en->length); 759 760 table_en->target = target; 761 table_en->dm_dev = dmv; 762 table_en->target_config = NULL; 763 764 /* 765 * There is a parameter string after dm_target_spec 766 * structure which points to /dev/wd0a 284 part of 767 * table. String str points to this text. This can be 768 * null and therefore it should be checked before we try to 769 * use it. 770 */ 771 param_dict = prop_dictionary_get(target_dict, DM_TABLE_PARAMS); 772 773 if (SLIST_EMPTY(tbl)) 774 /* insert this table to head */ 775 SLIST_INSERT_HEAD(tbl, table_en, next); 776 else 777 SLIST_INSERT_AFTER(last_table, table_en, next); 778 779 /* 780 * Params string is different for every target, 781 * therfore I have to pass it to target init 782 * routine and parse parameters there. 783 */ 784 if ((ret = target->init(dmv, &table_en->target_config, 785 param_dict)) != 0) { 786 787 dm_table_release(&dmv->table_head, DM_TABLE_INACTIVE); 788 dm_table_destroy(&dmv->table_head, DM_TABLE_INACTIVE); 789 790 dm_dev_unbusy(dmv); 791 return ret; 792 } 793 last_table = table_en; 794 } 795 prop_object_iterator_release(iter); 796 797 DM_ADD_FLAG(flags, DM_INACTIVE_PRESENT_FLAG); 798 atomic_or_32(&dmv->flags, DM_INACTIVE_PRESENT_FLAG); 799 800 dm_table_release(&dmv->table_head, DM_TABLE_INACTIVE); 801 dm_dev_unbusy(dmv); 802 return 0; 803 } 804 805 /* 806 * Get description of all tables loaded to device from kernel 807 * and send it to libdevmapper. 808 * 809 * Output dictionary for every table: 810 * 811 * <key>cmd_data</key> 812 * <array> 813 * <dict> 814 * <key>type<key> 815 * <string>...</string> 816 * 817 * <key>start</key> 818 * <integer>...</integer> 819 * 820 * <key>length</key> 821 * <integer>...</integer> 822 * 823 * <key>params</key> 824 * <string>...</string> 825 * </dict> 826 * </array> 827 * 828 */ 829 int 830 dm_table_status_ioctl(prop_dictionary_t dm_dict) 831 { 832 dm_dev_t *dmv; 833 dm_table_t *tbl; 834 dm_table_entry_t *table_en; 835 836 prop_array_t cmd_array; 837 prop_dictionary_t target_dict; 838 839 uint32_t rec_size, minor; 840 841 const char *name, *uuid; 842 char *params; 843 int flags; 844 845 dmv = NULL; 846 uuid = NULL; 847 name = NULL; 848 params = NULL; 849 flags = 0; 850 rec_size = 0; 851 852 prop_dictionary_get_cstring_nocopy(dm_dict, DM_IOCTL_NAME, &name); 853 prop_dictionary_get_cstring_nocopy(dm_dict, DM_IOCTL_UUID, &uuid); 854 prop_dictionary_get_uint32(dm_dict, DM_IOCTL_FLAGS, &flags); 855 prop_dictionary_get_uint32(dm_dict, DM_IOCTL_MINOR, &minor); 856 857 cmd_array = prop_array_create(); 858 859 if ((dmv = dm_dev_lookup(name, uuid, minor)) == NULL){ 860 DM_REMOVE_FLAG(flags, DM_EXISTS_FLAG); 861 return ENOENT; 862 } 863 864 if (dm_table_get_target_count(&dmv->table_head, DM_TABLE_ACTIVE)) 865 DM_ADD_FLAG(flags, DM_ACTIVE_PRESENT_FLAG); 866 else { 867 DM_REMOVE_FLAG(flags, DM_ACTIVE_PRESENT_FLAG); 868 869 if (dm_table_get_target_count(&dmv->table_head, DM_TABLE_INACTIVE)) 870 DM_ADD_FLAG(flags, DM_INACTIVE_PRESENT_FLAG); 871 else { 872 DM_REMOVE_FLAG(flags, DM_INACTIVE_PRESENT_FLAG); 873 } 874 } 875 876 if (dmv->flags & DM_SUSPEND_FLAG) 877 DM_ADD_FLAG(flags, DM_SUSPEND_FLAG); 878 879 prop_dictionary_set_uint32(dm_dict, DM_IOCTL_MINOR, dmv->minor); 880 881 /* I should use mutex here and not rwlock there can be IO operation 882 during this ioctl on device. */ 883 884 aprint_debug("Status of device tables: %s--%d\n", 885 name, dmv->table_head.cur_active_table); 886 887 tbl = dm_table_get_entry(&dmv->table_head, DM_TABLE_ACTIVE); 888 889 SLIST_FOREACH(table_en, tbl, next) 890 { 891 target_dict = prop_dictionary_create(); 892 aprint_debug("%016" PRIu64 ", length %016" PRIu64 893 ", target %s\n", table_en->start, table_en->length, 894 table_en->target->name); 895 896 prop_dictionary_set_uint64(target_dict, DM_TABLE_START, 897 table_en->start); 898 prop_dictionary_set_uint64(target_dict, DM_TABLE_LENGTH, 899 table_en->length); 900 901 prop_dictionary_set_cstring(target_dict, DM_TABLE_TYPE, 902 table_en->target->name); 903 904 /* dm_table_get_cur_actv.table ?? */ 905 prop_dictionary_set_int32(target_dict, DM_TABLE_STAT, 906 dmv->table_head.cur_active_table); 907 908 if (flags |= DM_STATUS_TABLE_FLAG) { 909 params = table_en->target->status 910 (table_en->target_config); 911 912 if(params != NULL){ 913 prop_dictionary_set_cstring(target_dict, 914 DM_TABLE_PARAMS, params); 915 916 kmem_free(params, strlen(params) + 1); 917 } 918 } 919 920 prop_array_add(cmd_array, target_dict); 921 prop_object_release(target_dict); 922 } 923 924 dm_table_release(&dmv->table_head, DM_TABLE_ACTIVE); 925 dm_dev_unbusy(dmv); 926 927 prop_dictionary_set(dm_dict, DM_IOCTL_CMD_DATA, cmd_array); 928 prop_object_release(cmd_array); 929 930 return 0; 931 } 932 933 934 /* 935 * For every call I have to set kernel driver version. 936 * Because I can have commands supported only in other 937 * newer/later version. This routine is called for every 938 * ioctl command. 939 */ 940 int 941 dm_check_version(prop_dictionary_t dm_dict) 942 { 943 size_t i; 944 int dm_version[3]; 945 prop_array_t ver; 946 947 ver = prop_dictionary_get(dm_dict, DM_IOCTL_VERSION); 948 949 for(i=0; i < 3; i++) 950 prop_array_get_uint32(ver, i, &dm_version[i]); 951 952 if (DM_VERSION_MAJOR != dm_version[0] || DM_VERSION_MINOR < dm_version[1]){ 953 aprint_debug("libdevmapper/kernel version mismatch " 954 "kernel: %d.%d.%d libdevmapper: %d.%d.%d\n", 955 DM_VERSION_MAJOR, DM_VERSION_MINOR, DM_VERSION_PATCHLEVEL, 956 dm_version[0], dm_version[1], dm_version[2]); 957 958 return EIO; 959 } 960 961 prop_array_set_uint32(ver, 0, DM_VERSION_MAJOR); 962 prop_array_set_uint32(ver, 1, DM_VERSION_MINOR); 963 prop_array_set_uint32(ver, 2, DM_VERSION_PATCHLEVEL); 964 965 return 0; 966 } 967