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