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