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