1 /* $NetBSD: dm_ioctl.c,v 1.49 2019/12/23 16:17:35 tkusumi 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.49 2019/12/23 16:17:35 tkusumi 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_cstring_nocopy(dm_dict, DM_IOCTL_NAME, &name); 206 prop_dictionary_get_cstring_nocopy(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_cstring_nocopy(dm_dict, DM_IOCTL_NAME, &name); 327 prop_dictionary_get_cstring_nocopy(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_cstring_nocopy(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_cstring(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_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 /* 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_cstring_nocopy(dm_dict, DM_IOCTL_NAME, &name); 419 prop_dictionary_get_cstring_nocopy(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_cstring(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_cstring_nocopy(dm_dict, DM_IOCTL_NAME, &name); 473 prop_dictionary_get_cstring_nocopy(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_cstring_nocopy(dm_dict, DM_IOCTL_NAME, &name); 518 prop_dictionary_get_cstring_nocopy(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_cstring_nocopy(dm_dict, DM_IOCTL_NAME, &name); 574 prop_dictionary_get_cstring_nocopy(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_cstring_nocopy(dm_dict, DM_IOCTL_NAME, &name); 619 prop_dictionary_get_cstring_nocopy(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_cstring(dm_dict, DM_IOCTL_NAME, dmv->name); 632 prop_dictionary_set_cstring(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_cstring_nocopy(dm_dict, DM_IOCTL_NAME, &name); 724 prop_dictionary_get_cstring_nocopy(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 char *str = NULL; 757 758 prop_dictionary_get_cstring_nocopy(target_dict, 759 DM_TABLE_TYPE, &type); 760 /* 761 * If we want to deny table with 2 or more different 762 * target we should do it here 763 */ 764 if (((target = dm_target_lookup(type)) == NULL) && 765 ((target = dm_target_autoload(type)) == NULL)) { 766 dm_table_release(&dmv->table_head, DM_TABLE_INACTIVE); 767 dm_dev_unbusy(dmv); 768 prop_object_iterator_release(iter); 769 return ENOENT; 770 } 771 table_en = kmem_alloc(sizeof(dm_table_entry_t), KM_SLEEP); 772 prop_dictionary_get_uint64(target_dict, DM_TABLE_START, 773 &table_en->start); 774 prop_dictionary_get_uint64(target_dict, DM_TABLE_LENGTH, 775 &table_en->length); 776 777 table_en->target = target; 778 table_en->dm_dev = dmv; 779 table_en->target_config = NULL; 780 TAILQ_INIT(&table_en->pdev_maps); 781 782 /* 783 * There is a parameter string after dm_target_spec 784 * structure which points to /dev/wd0a 284 part of 785 * table. String str points to this text. This can be 786 * null and therefore it should be checked before we try to 787 * use it. 788 */ 789 prop_dictionary_get_cstring(target_dict, 790 DM_TABLE_PARAMS, (char **) &str); 791 792 if (SLIST_EMPTY(tbl) || last_table == NULL) 793 /* insert this table to head */ 794 SLIST_INSERT_HEAD(tbl, table_en, next); 795 else 796 SLIST_INSERT_AFTER(last_table, table_en, next); 797 798 if ((ret = dm_table_init(target, table_en, str)) != 0) { 799 dm_table_release(&dmv->table_head, DM_TABLE_INACTIVE); 800 dm_table_destroy(&dmv->table_head, DM_TABLE_INACTIVE); 801 free(str, M_TEMP); 802 803 dm_dev_unbusy(dmv); 804 prop_object_iterator_release(iter); 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 static int 821 dm_table_init(dm_target_t *target, dm_table_entry_t *table_en, char *params) 822 { 823 int i, n, ret, argc; 824 char **ap, **argv; 825 826 if (params == NULL) 827 return EINVAL; 828 829 n = target->max_argc; 830 if (n) 831 aprint_debug("Max argc %d for %s target\n", n, target->name); 832 else 833 n = 32; /* large enough for most targets */ 834 835 argv = kmem_alloc(sizeof(*argv) * n, KM_SLEEP); 836 837 for (ap = argv; 838 ap < &argv[n] && (*ap = strsep(¶ms, " \t")) != NULL;) { 839 if (**ap != '\0') 840 ap++; 841 } 842 argc = ap - argv; 843 844 for (i = 0; i < argc; i++) 845 aprint_debug("DM: argv[%d] = \"%s\"\n", i, argv[i]); 846 847 KASSERT(target->init); 848 ret = target->init(table_en, argc, argv); 849 850 kmem_free(argv, sizeof(*argv) * n); 851 852 return ret; 853 } 854 855 /* 856 * Get description of all tables loaded to device from kernel 857 * and send it to libdevmapper. 858 * 859 * Output dictionary for every table: 860 * 861 * <key>cmd_data</key> 862 * <array> 863 * <dict> 864 * <key>type<key> 865 * <string>...</string> 866 * 867 * <key>start</key> 868 * <integer>...</integer> 869 * 870 * <key>length</key> 871 * <integer>...</integer> 872 * 873 * <key>params</key> 874 * <string>...</string> 875 * </dict> 876 * </array> 877 */ 878 int 879 dm_table_status_ioctl(prop_dictionary_t dm_dict) 880 { 881 dm_dev_t *dmv; 882 dm_table_t *tbl; 883 dm_table_entry_t *table_en; 884 885 prop_array_t cmd_array; 886 prop_dictionary_t target_dict; 887 888 uint32_t minor, flags; 889 const char *name, *uuid; 890 int table_type; 891 892 uuid = NULL; 893 name = NULL; 894 flags = 0; 895 896 prop_dictionary_get_cstring_nocopy(dm_dict, DM_IOCTL_NAME, &name); 897 prop_dictionary_get_cstring_nocopy(dm_dict, DM_IOCTL_UUID, &uuid); 898 prop_dictionary_get_uint32(dm_dict, DM_IOCTL_FLAGS, &flags); 899 prop_dictionary_get_uint32(dm_dict, DM_IOCTL_MINOR, &minor); 900 901 cmd_array = prop_array_create(); 902 903 if ((dmv = dm_dev_lookup(name, uuid, minor)) == NULL) { 904 DM_REMOVE_FLAG(flags, DM_EXISTS_FLAG); 905 return ENOENT; 906 } 907 /* 908 * if DM_QUERY_INACTIVE_TABLE_FLAG is passed we need to query 909 * INACTIVE TABLE 910 */ 911 if (flags & DM_QUERY_INACTIVE_TABLE_FLAG) 912 table_type = DM_TABLE_INACTIVE; 913 else 914 table_type = DM_TABLE_ACTIVE; 915 916 if (dm_table_get_target_count(&dmv->table_head, DM_TABLE_ACTIVE)) { 917 DM_ADD_FLAG(flags, DM_ACTIVE_PRESENT_FLAG); 918 } else { 919 DM_REMOVE_FLAG(flags, DM_ACTIVE_PRESENT_FLAG); 920 921 if (dm_table_get_target_count(&dmv->table_head, DM_TABLE_INACTIVE)) 922 DM_ADD_FLAG(flags, DM_INACTIVE_PRESENT_FLAG); 923 else 924 DM_REMOVE_FLAG(flags, DM_INACTIVE_PRESENT_FLAG); 925 } 926 927 if (dmv->flags & DM_SUSPEND_FLAG) 928 DM_ADD_FLAG(flags, DM_SUSPEND_FLAG); 929 930 prop_dictionary_set_uint32(dm_dict, DM_IOCTL_MINOR, dmv->minor); 931 932 aprint_debug("Status of device tables: %s--%d\n", 933 name, dmv->table_head.cur_active_table); 934 935 tbl = dm_table_get_entry(&dmv->table_head, table_type); 936 937 SLIST_FOREACH(table_en, tbl, next) { 938 char *params; 939 int is_table; 940 941 target_dict = prop_dictionary_create(); 942 aprint_debug("%016" PRIu64 ", length %016" PRIu64 943 ", target %s\n", table_en->start, table_en->length, 944 table_en->target->name); 945 946 prop_dictionary_set_uint64(target_dict, DM_TABLE_START, 947 table_en->start); 948 prop_dictionary_set_uint64(target_dict, DM_TABLE_LENGTH, 949 table_en->length); 950 951 prop_dictionary_set_cstring(target_dict, DM_TABLE_TYPE, 952 table_en->target->name); 953 954 /* dm_table_get_cur_actv.table ?? */ 955 prop_dictionary_set_int32(target_dict, DM_TABLE_STAT, 956 dmv->table_head.cur_active_table); 957 958 /* 959 * Explicitly clear DM_TABLE_PARAMS to prevent dmsetup(8) from 960 * printing junk when DM_TABLE_PARAMS was never initialized. 961 */ 962 prop_dictionary_set_cstring(target_dict, DM_TABLE_PARAMS, ""); 963 964 is_table = (flags & DM_STATUS_TABLE_FLAG) ? 1 : 0; 965 if (is_table && table_en->target->table) 966 params = table_en->target->table( 967 table_en->target_config); 968 else if (!is_table && table_en->target->info) 969 params = table_en->target->info( 970 table_en->target_config); 971 else 972 params = NULL; 973 974 if (params != NULL) { 975 prop_dictionary_set_cstring(target_dict, 976 DM_TABLE_PARAMS, params); 977 kmem_free(params, DM_MAX_PARAMS_SIZE); 978 } 979 980 prop_array_add(cmd_array, target_dict); 981 prop_object_release(target_dict); 982 } 983 984 dm_table_release(&dmv->table_head, table_type); 985 dm_dev_unbusy(dmv); 986 987 prop_dictionary_set_uint32(dm_dict, DM_IOCTL_FLAGS, flags); 988 prop_dictionary_set(dm_dict, DM_IOCTL_CMD_DATA, cmd_array); 989 prop_object_release(cmd_array); 990 991 return 0; 992 } 993 994 /* 995 * For every call I have to set kernel driver version. 996 * Because I can have commands supported only in other 997 * newer/later version. This routine is called for every 998 * ioctl command. 999 */ 1000 int 1001 dm_check_version(prop_dictionary_t dm_dict) 1002 { 1003 int i; 1004 uint32_t dm_version[3]; 1005 prop_array_t ver; 1006 1007 ver = prop_dictionary_get(dm_dict, DM_IOCTL_VERSION); 1008 1009 for (i = 0; i < 3; i++) 1010 prop_array_get_uint32(ver, i, &dm_version[i]); 1011 1012 if (DM_VERSION_MAJOR != dm_version[0] || DM_VERSION_MINOR < dm_version[1]) { 1013 aprint_debug("libdevmapper/kernel version mismatch " 1014 "kernel: %d.%d.%d libdevmapper: %d.%d.%d\n", 1015 DM_VERSION_MAJOR, DM_VERSION_MINOR, DM_VERSION_PATCHLEVEL, 1016 dm_version[0], dm_version[1], dm_version[2]); 1017 1018 return EIO; 1019 } 1020 prop_array_set_uint32(ver, 0, DM_VERSION_MAJOR); 1021 prop_array_set_uint32(ver, 1, DM_VERSION_MINOR); 1022 prop_array_set_uint32(ver, 2, DM_VERSION_PATCHLEVEL); 1023 1024 prop_dictionary_set(dm_dict, DM_IOCTL_VERSION, ver); 1025 1026 return 0; 1027 } 1028