1 /* $NetBSD: device-mapper.c,v 1.64 2022/03/31 19:30:15 pgoyette Exp $ */ 2 3 /* 4 * Copyright (c) 2010 The NetBSD Foundation, Inc. 5 * All rights reserved. 6 * 7 * This code is derived from software contributed to The NetBSD Foundation 8 * by Adam Hamsik. 9 * 10 * Redistribution and use in source and binary forms, with or without 11 * modification, are permitted provided that the following conditions 12 * are met: 13 * 1. Redistributions of source code must retain the above copyright 14 * notice, this list of conditions and the following disclaimer. 15 * 2. Redistributions in binary form must reproduce the above copyright 16 * notice, this list of conditions and the following disclaimer in the 17 * documentation and/or other materials provided with the distribution. 18 * 19 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS 20 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 21 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 22 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS 23 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 24 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 25 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 26 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 27 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 28 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 29 * POSSIBILITY OF SUCH DAMAGE. 30 */ 31 32 /* 33 * I want to say thank you to all people who helped me with this project. 34 */ 35 36 #include <sys/types.h> 37 #include <sys/param.h> 38 #include <sys/buf.h> 39 #include <sys/conf.h> 40 #include <sys/device.h> 41 #include <sys/dkio.h> 42 #include <sys/disk.h> 43 #include <sys/disklabel.h> 44 #include <sys/ioctl.h> 45 #include <sys/ioccom.h> 46 #include <sys/kmem.h> 47 #include <sys/kauth.h> 48 49 #include "netbsd-dm.h" 50 #include "dm.h" 51 #include "ioconf.h" 52 53 static dev_type_open(dmopen); 54 static dev_type_close(dmclose); 55 static dev_type_read(dmread); 56 static dev_type_write(dmwrite); 57 static dev_type_ioctl(dmioctl); 58 static dev_type_strategy(dmstrategy); 59 static dev_type_size(dmsize); 60 61 /* attach and detach routines */ 62 #ifdef _MODULE 63 static int dmdestroy(void); 64 #endif 65 66 static void dm_doinit(void); 67 68 static int dm_cmd_to_fun(prop_dictionary_t); 69 static int disk_ioctl_switch(dev_t, unsigned long, void *); 70 static void dmminphys(struct buf *); 71 72 /* CF attach/detach functions used for power management */ 73 static int dm_detach(device_t, int); 74 static void dm_attach(device_t, device_t, void *); 75 static int dm_match(device_t, cfdata_t, void *); 76 77 /* ***Variable-definitions*** */ 78 const struct bdevsw dm_bdevsw = { 79 .d_open = dmopen, 80 .d_close = dmclose, 81 .d_strategy = dmstrategy, 82 .d_ioctl = dmioctl, 83 .d_dump = nodump, 84 .d_psize = dmsize, 85 .d_discard = nodiscard, 86 .d_flag = D_DISK | D_MPSAFE 87 }; 88 89 const struct cdevsw dm_cdevsw = { 90 .d_open = dmopen, 91 .d_close = dmclose, 92 .d_read = dmread, 93 .d_write = dmwrite, 94 .d_ioctl = dmioctl, 95 .d_stop = nostop, 96 .d_tty = notty, 97 .d_poll = nopoll, 98 .d_mmap = nommap, 99 .d_kqfilter = nokqfilter, 100 .d_discard = nodiscard, 101 .d_flag = D_DISK | D_MPSAFE 102 }; 103 104 const struct dkdriver dmdkdriver = { 105 .d_strategy = dmstrategy 106 }; 107 108 CFATTACH_DECL3_NEW(dm, 0, 109 dm_match, dm_attach, dm_detach, NULL, NULL, NULL, 110 DVF_DETACH_SHUTDOWN); 111 112 /* 113 * This structure is used to translate command sent to kernel driver in 114 * <key>command</key> 115 * <value></value> 116 * to function which I can call, and if the command is allowed for 117 * non-superusers. 118 */ 119 /* 120 * This array is used to translate cmd to function pointer. 121 * 122 * Interface between libdevmapper and lvm2tools uses different 123 * names for one IOCTL call because libdevmapper do another thing 124 * then. When I run "info" or "mknodes" libdevmapper will send same 125 * ioctl to kernel but will do another things in userspace. 126 * 127 */ 128 static const struct cmd_function { 129 const char *cmd; 130 int (*fn)(prop_dictionary_t); 131 int allowed; 132 } cmd_fn[] = { 133 { .cmd = "version", .fn = NULL, .allowed = 1 }, 134 { .cmd = "targets", .fn = dm_list_versions_ioctl, .allowed = 1 }, 135 { .cmd = "create", .fn = dm_dev_create_ioctl, .allowed = 0 }, 136 { .cmd = "info", .fn = dm_dev_status_ioctl, .allowed = 1 }, 137 { .cmd = "mknodes", .fn = dm_dev_status_ioctl, .allowed = 1 }, 138 { .cmd = "names", .fn = dm_dev_list_ioctl, .allowed = 1 }, 139 { .cmd = "suspend", .fn = dm_dev_suspend_ioctl, .allowed = 0 }, 140 { .cmd = "remove", .fn = dm_dev_remove_ioctl, .allowed = 0 }, 141 { .cmd = "rename", .fn = dm_dev_rename_ioctl, .allowed = 0 }, 142 { .cmd = "resume", .fn = dm_dev_resume_ioctl, .allowed = 0 }, 143 { .cmd = "clear", .fn = dm_table_clear_ioctl, .allowed = 0 }, 144 { .cmd = "deps", .fn = dm_table_deps_ioctl, .allowed = 1 }, 145 { .cmd = "reload", .fn = dm_table_load_ioctl, .allowed = 0 }, 146 { .cmd = "status", .fn = dm_table_status_ioctl, .allowed = 1 }, 147 { .cmd = "table", .fn = dm_table_status_ioctl, .allowed = 1 }, 148 { .cmd = NULL, .fn = NULL, .allowed = 0 }, 149 }; 150 151 #ifdef _MODULE 152 #include <sys/module.h> 153 154 /* Autoconf defines */ 155 CFDRIVER_DECL(dm, DV_DISK, NULL); 156 157 MODULE(MODULE_CLASS_DRIVER, dm, "dk_subr"); 158 159 /* New module handle routine */ 160 static int 161 dm_modcmd(modcmd_t cmd, void *arg) 162 { 163 #ifdef _MODULE 164 int error; 165 devmajor_t bmajor, cmajor; 166 167 error = 0; 168 bmajor = -1; 169 cmajor = -1; 170 171 switch (cmd) { 172 case MODULE_CMD_INIT: 173 error = devsw_attach(dm_cd.cd_name, &dm_bdevsw, &bmajor, 174 &dm_cdevsw, &cmajor); 175 if (error == EEXIST) 176 error = 0; 177 if (error) 178 break; 179 180 error = config_cfdriver_attach(&dm_cd); 181 if (error) { 182 devsw_detach(&dm_bdevsw, &dm_cdevsw); 183 return error; 184 } 185 186 error = config_cfattach_attach(dm_cd.cd_name, &dm_ca); 187 if (error) { 188 config_cfdriver_detach(&dm_cd); 189 devsw_detach(&dm_bdevsw, &dm_cdevsw); 190 aprint_error("%s: unable to register cfattach\n", 191 dm_cd.cd_name); 192 return error; 193 } 194 195 dm_doinit(); 196 break; 197 case MODULE_CMD_FINI: 198 /* 199 * Disable unloading of dm module if there are any devices 200 * defined in driver. This is probably too strong we need 201 * to disable auto-unload only if there is mounted dm device 202 * present. 203 */ 204 if (dm_dev_counter > 0) 205 return EBUSY; 206 /* race window here */ 207 208 error = dmdestroy(); 209 if (error) 210 break; 211 212 config_cfdriver_detach(&dm_cd); 213 config_cfattach_detach(dm_cd.cd_name, &dm_ca); 214 215 devsw_detach(&dm_bdevsw, &dm_cdevsw); 216 break; 217 case MODULE_CMD_STAT: 218 return ENOTTY; 219 default: 220 return ENOTTY; 221 } 222 223 return error; 224 #else 225 return ENOTTY; 226 #endif 227 } 228 #endif /* _MODULE */ 229 230 /* 231 * dm_match: 232 * 233 * Autoconfiguration match function for pseudo-device glue. 234 */ 235 static int 236 dm_match(device_t parent, cfdata_t match, void *aux) 237 { 238 239 /* Pseudo-device; always present. */ 240 return 1; 241 } 242 243 /* 244 * dm_attach: 245 * 246 * Autoconfiguration attach function for pseudo-device glue. 247 */ 248 static void 249 dm_attach(device_t parent, device_t self, void *aux) 250 { 251 252 if (!pmf_device_register(self, NULL, NULL)) 253 aprint_error_dev(self, "couldn't establish power handler\n"); 254 } 255 256 /* 257 * dm_detach: 258 * 259 * Autoconfiguration detach function for pseudo-device glue. 260 * This routine is called by dm_ioctl::dm_dev_remove_ioctl and by autoconf to 261 * remove devices created in device-mapper. 262 */ 263 static int 264 dm_detach(device_t self, int flags) 265 { 266 bool busy; 267 dm_dev_t *dmv; 268 269 dmv = dm_dev_lookup(NULL, NULL, device_unit(self)); 270 mutex_enter(&dmv->diskp->dk_openlock); 271 busy = (dmv->diskp->dk_openmask != 0 && (flags & DETACH_FORCE) == 0); 272 mutex_exit(&dmv->diskp->dk_openlock); 273 dm_dev_unbusy(dmv); 274 if (busy) 275 return EBUSY; 276 277 pmf_device_deregister(self); 278 279 /* Detach device from global device list */ 280 if ((dmv = dm_dev_detach(self)) == NULL) 281 return ENOENT; 282 283 /* Destroy active table first. */ 284 dm_table_destroy(&dmv->table_head, DM_TABLE_ACTIVE); 285 286 /* Destroy inactive table if exits, too. */ 287 dm_table_destroy(&dmv->table_head, DM_TABLE_INACTIVE); 288 289 dm_table_head_destroy(&dmv->table_head); 290 291 /* Destroy disk device structure */ 292 disk_detach(dmv->diskp); 293 disk_destroy(dmv->diskp); 294 295 /* Destroy device */ 296 dm_dev_free(dmv); 297 298 /* Decrement device counter After removing device */ 299 atomic_dec_32(&dm_dev_counter); 300 301 return 0; 302 } 303 304 static void 305 dm_doinit(void) 306 { 307 308 dm_target_init(); 309 dm_dev_init(); 310 dm_pdev_init(); 311 } 312 313 /* attach routine */ 314 void 315 dmattach(int n) 316 { 317 int error; 318 319 error = config_cfattach_attach(dm_cd.cd_name, &dm_ca); 320 if (error) 321 aprint_error("%s: unable to register cfattach\n", 322 dm_cd.cd_name); 323 else 324 dm_doinit(); 325 } 326 327 #ifdef _MODULE 328 /* Destroy routine */ 329 static int 330 dmdestroy(void) 331 { 332 int error; 333 334 error = config_cfattach_detach(dm_cd.cd_name, &dm_ca); 335 if (error) 336 return error; 337 338 dm_dev_destroy(); 339 dm_pdev_destroy(); 340 dm_target_destroy(); 341 342 return 0; 343 } 344 #endif /* _MODULE */ 345 346 static int 347 dmopen(dev_t dev, int flags, int mode, struct lwp *l) 348 { 349 dm_dev_t *dmv; 350 struct disk *dk; 351 352 dmv = dm_dev_lookup(NULL, NULL, minor(dev)); 353 if (dmv) { 354 dk = dmv->diskp; 355 mutex_enter(&dk->dk_openlock); 356 switch (mode) { 357 case S_IFCHR: 358 dk->dk_copenmask |= 1; 359 break; 360 case S_IFBLK: 361 dk->dk_bopenmask |= 1; 362 break; 363 } 364 dk->dk_openmask = dk->dk_copenmask | dk->dk_bopenmask; 365 mutex_exit(&dk->dk_openlock); 366 dm_dev_unbusy(dmv); 367 } 368 369 aprint_debug("dm open routine called %" PRIu32 "\n", minor(dev)); 370 return 0; 371 } 372 373 static int 374 dmclose(dev_t dev, int flags, int mode, struct lwp *l) 375 { 376 dm_dev_t *dmv; 377 struct disk *dk; 378 379 aprint_debug("dm close routine called %" PRIu32 "\n", minor(dev)); 380 381 dmv = dm_dev_lookup(NULL, NULL, minor(dev)); 382 if (dmv) { 383 dk = dmv->diskp; 384 mutex_enter(&dk->dk_openlock); 385 switch (mode) { 386 case S_IFCHR: 387 dk->dk_copenmask &= ~1; 388 break; 389 case S_IFBLK: 390 dk->dk_bopenmask &= ~1; 391 break; 392 } 393 dk->dk_openmask = dk->dk_copenmask | dk->dk_bopenmask; 394 mutex_exit(&dk->dk_openlock); 395 dm_dev_unbusy(dmv); 396 } 397 return 0; 398 } 399 400 401 static int 402 dmioctl(dev_t dev, const u_long cmd, void *data, int flag, struct lwp *l) 403 { 404 int r; 405 prop_dictionary_t dm_dict_in; 406 407 aprint_debug("dmioctl called\n"); 408 KASSERT(data != NULL); 409 410 if ((r = disk_ioctl_switch(dev, cmd, data)) == ENOTTY) { 411 struct plistref *pref = (struct plistref *)data; 412 413 switch(cmd) { 414 case NETBSD_DM_IOCTL: 415 aprint_debug("dm NETBSD_DM_IOCTL called\n"); 416 break; 417 default: 418 aprint_debug("dm unknown ioctl called\n"); 419 return ENOTTY; 420 break; /* NOT REACHED */ 421 } 422 423 if ((r = prop_dictionary_copyin_ioctl(pref, cmd, &dm_dict_in)) 424 != 0) 425 return r; 426 427 if ((r = dm_check_version(dm_dict_in)) != 0) 428 goto cleanup_exit; 429 430 /* run ioctl routine */ 431 if ((r = dm_cmd_to_fun(dm_dict_in)) != 0) 432 goto cleanup_exit; 433 434 cleanup_exit: 435 r = prop_dictionary_copyout_ioctl(pref, cmd, dm_dict_in); 436 prop_object_release(dm_dict_in); 437 } 438 439 return r; 440 } 441 442 /* 443 * Translate command sent from libdevmapper to func. 444 */ 445 static int 446 dm_cmd_to_fun(prop_dictionary_t dm_dict) 447 { 448 int i, r; 449 prop_string_t command; 450 451 if ((command = prop_dictionary_get(dm_dict, DM_IOCTL_COMMAND)) == NULL) 452 return EINVAL; 453 454 for (i = 0; cmd_fn[i].cmd != NULL; i++) 455 if (prop_string_equals_string(command, cmd_fn[i].cmd)) 456 break; 457 458 if (!cmd_fn[i].allowed && 459 (r = kauth_authorize_system(kauth_cred_get(), 460 KAUTH_SYSTEM_DEVMAPPER, 0, NULL, NULL, NULL)) != 0) 461 return r; 462 463 if (cmd_fn[i].cmd == NULL) 464 return EINVAL; 465 466 aprint_debug("ioctl %s called %p\n", cmd_fn[i].cmd, cmd_fn[i].fn); 467 if (cmd_fn[i].fn == NULL) 468 return 0; 469 470 return cmd_fn[i].fn(dm_dict); 471 } 472 473 /* 474 * Check for disk specific ioctls. 475 */ 476 static int 477 disk_ioctl_switch(dev_t dev, unsigned long cmd, void *data) 478 { 479 dm_dev_t *dmv; 480 481 /* disk ioctls make sense only on block devices */ 482 if (minor(dev) == 0) 483 return ENOTTY; 484 485 switch(cmd) { 486 case DIOCGWEDGEINFO: 487 { 488 struct dkwedge_info *dkw = (void *) data; 489 490 if ((dmv = dm_dev_lookup(NULL, NULL, minor(dev))) == NULL) 491 return ENODEV; 492 493 aprint_debug("DIOCGWEDGEINFO ioctl called\n"); 494 495 strlcpy(dkw->dkw_devname, dmv->name, 16); 496 strlcpy(dkw->dkw_wname, dmv->name, DM_NAME_LEN); 497 strlcpy(dkw->dkw_parent, dmv->name, 16); 498 499 dkw->dkw_offset = 0; 500 dm_table_disksize(&dmv->table_head, &dkw->dkw_size, NULL); 501 strcpy(dkw->dkw_ptype, DKW_PTYPE_FFS); 502 503 dm_dev_unbusy(dmv); 504 break; 505 } 506 case DIOCGDISKINFO: 507 { 508 struct plistref *pref = (struct plistref *) data; 509 510 if ((dmv = dm_dev_lookup(NULL, NULL, minor(dev))) == NULL) 511 return ENODEV; 512 513 aprint_debug("DIOCGDISKINFO ioctl called\n"); 514 515 if (dmv->diskp->dk_info == NULL) { 516 dm_dev_unbusy(dmv); 517 return ENOTSUP; 518 } else 519 prop_dictionary_copyout_ioctl(pref, cmd, 520 dmv->diskp->dk_info); 521 522 dm_dev_unbusy(dmv); 523 break; 524 } 525 case DIOCCACHESYNC: 526 { 527 dm_table_entry_t *table_en; 528 dm_table_t *tbl; 529 530 if ((dmv = dm_dev_lookup(NULL, NULL, minor(dev))) == NULL) 531 return ENODEV; 532 533 aprint_debug("DIOCCACHESYNC ioctl called\n"); 534 535 /* Select active table */ 536 tbl = dm_table_get_entry(&dmv->table_head, DM_TABLE_ACTIVE); 537 538 /* 539 * Call sync target routine for all table entries. Target sync 540 * routine basically call DIOCCACHESYNC on underlying devices. 541 */ 542 SLIST_FOREACH(table_en, tbl, next) 543 if (table_en->target->sync) 544 table_en->target->sync(table_en); 545 dm_table_release(&dmv->table_head, DM_TABLE_ACTIVE); 546 dm_dev_unbusy(dmv); 547 break; 548 } 549 case DIOCGSECTORSIZE: 550 { 551 unsigned int secsize, *valp = data; 552 553 if ((dmv = dm_dev_lookup(NULL, NULL, minor(dev))) == NULL) 554 return ENODEV; 555 556 aprint_debug("DIOCGSECTORSIZE ioctl called\n"); 557 558 dm_table_disksize(&dmv->table_head, NULL, &secsize); 559 *valp = secsize; 560 561 dm_dev_unbusy(dmv); 562 break; 563 } 564 case DIOCGMEDIASIZE: 565 { 566 off_t *valp = data; 567 uint64_t numsec; 568 unsigned secsize; 569 570 if ((dmv = dm_dev_lookup(NULL, NULL, minor(dev))) == NULL) 571 return ENODEV; 572 573 aprint_debug("DIOCGMEDIASIZE ioctl called\n"); 574 575 dm_table_disksize(&dmv->table_head, &numsec, &secsize); 576 *valp = (off_t) secsize * numsec; 577 578 dm_dev_unbusy(dmv); 579 break; 580 } 581 default: 582 aprint_debug("unknown disk_ioctl called\n"); 583 return ENOTTY; 584 break; /* NOT REACHED */ 585 } 586 587 return 0; 588 } 589 590 /* 591 * Do all IO operations on dm logical devices. 592 */ 593 static void 594 dmstrategy(struct buf *bp) 595 { 596 dm_dev_t *dmv; 597 dm_table_t *tbl; 598 dm_table_entry_t *table_en; 599 struct buf *nestbuf; 600 601 uint64_t buf_start, buf_len, issued_len; 602 uint64_t table_start, table_end; 603 uint64_t start, end; 604 605 buf_start = bp->b_blkno * DEV_BSIZE; 606 buf_len = bp->b_bcount; 607 608 table_end = 0; 609 issued_len = 0; 610 611 if ((dmv = dm_dev_lookup(NULL, NULL, minor(bp->b_dev))) == NULL) { 612 bp->b_error = EIO; 613 bp->b_resid = bp->b_bcount; 614 biodone(bp); 615 return; 616 } 617 618 if (bounds_check_with_mediasize(bp, DEV_BSIZE, 619 dm_table_size(&dmv->table_head)) <= 0) { 620 dm_dev_unbusy(dmv); 621 bp->b_resid = bp->b_bcount; 622 biodone(bp); 623 return; 624 } 625 626 /* 627 * disk(9) is part of device structure and it can't be used without 628 * mutual exclusion, use diskp_mtx until it will be fixed. 629 */ 630 mutex_enter(&dmv->diskp_mtx); 631 disk_busy(dmv->diskp); 632 mutex_exit(&dmv->diskp_mtx); 633 634 /* Select active table */ 635 tbl = dm_table_get_entry(&dmv->table_head, DM_TABLE_ACTIVE); 636 637 /* Nested buffers count down to zero therefore I have 638 to set bp->b_resid to maximal value. */ 639 bp->b_resid = bp->b_bcount; 640 641 /* 642 * Find out what tables I want to select. 643 */ 644 SLIST_FOREACH(table_en, tbl, next) { 645 /* I need number of bytes not blocks. */ 646 table_start = table_en->start * DEV_BSIZE; 647 /* 648 * I have to sub 1 from table_en->length to prevent 649 * off by one error 650 */ 651 table_end = table_start + table_en->length * DEV_BSIZE; 652 start = MAX(table_start, buf_start); 653 end = MIN(table_end, buf_start + buf_len); 654 655 aprint_debug("----------------------------------------\n"); 656 aprint_debug("table_start %010" PRIu64", table_end %010" 657 PRIu64 "\n", table_start, table_end); 658 aprint_debug("buf_start %010" PRIu64", buf_len %010" 659 PRIu64"\n", buf_start, buf_len); 660 aprint_debug("start-buf_start %010"PRIu64", end %010" 661 PRIu64"\n", start - buf_start, end); 662 aprint_debug("start %010" PRIu64", end %010" 663 PRIu64"\n", start, end); 664 aprint_debug("----------------------------------------\n"); 665 666 if (start < end) { 667 /* create nested buffer */ 668 nestbuf = getiobuf(NULL, true); 669 nestiobuf_setup(bp, nestbuf, start - buf_start, 670 end - start); 671 issued_len += end - start; 672 /* I need number of blocks. */ 673 nestbuf->b_blkno = (start - table_start) / DEV_BSIZE; 674 table_en->target->strategy(table_en, nestbuf); 675 } 676 } 677 678 if (issued_len < buf_len) 679 nestiobuf_done(bp, buf_len - issued_len, EINVAL); 680 681 mutex_enter(&dmv->diskp_mtx); 682 disk_unbusy(dmv->diskp, buf_len, bp ? (bp->b_flags & B_READ) : 0); 683 mutex_exit(&dmv->diskp_mtx); 684 685 dm_table_release(&dmv->table_head, DM_TABLE_ACTIVE); 686 dm_dev_unbusy(dmv); 687 } 688 689 690 static int 691 dmread(dev_t dev, struct uio *uio, int flag) 692 { 693 694 return physio(dmstrategy, NULL, dev, B_READ, dmminphys, uio); 695 } 696 697 static int 698 dmwrite(dev_t dev, struct uio *uio, int flag) 699 { 700 701 return physio(dmstrategy, NULL, dev, B_WRITE, dmminphys, uio); 702 } 703 704 static int 705 dmsize(dev_t dev) 706 { 707 dm_dev_t *dmv; 708 uint64_t size; 709 710 if ((dmv = dm_dev_lookup(NULL, NULL, minor(dev))) == NULL) 711 return -ENOENT; 712 713 size = dm_table_size(&dmv->table_head); 714 dm_dev_unbusy(dmv); 715 716 return size; 717 } 718 719 static void 720 dmminphys(struct buf *bp) 721 { 722 723 bp->b_bcount = MIN(bp->b_bcount, MAXPHYS); 724 } 725 726 void 727 dmgetproperties(struct disk *disk, dm_table_head_t *head) 728 { 729 uint64_t numsec; 730 unsigned int secsize; 731 struct disk_geom *dg; 732 733 dm_table_disksize(head, &numsec, &secsize); 734 735 dg = &disk->dk_geom; 736 737 memset(dg, 0, sizeof(*dg)); 738 dg->dg_secperunit = numsec; 739 dg->dg_secsize = secsize; 740 dg->dg_nsectors = 32; 741 dg->dg_ntracks = 64; 742 743 disk_set_info(NULL, disk, "ESDI"); 744 } 745 746 /* 747 * Transform char s to uint64_t offset number. 748 */ 749 uint64_t 750 atoi64(const char *s) 751 { 752 uint64_t n; 753 n = 0; 754 755 while (*s != '\0') { 756 if (!isdigit(*s)) 757 break; 758 759 n = (10 * n) + (*s - '0'); 760 s++; 761 } 762 763 return n; 764 } 765