1 /* $NetBSD: device-mapper.c,v 1.62 2021/05/07 09:54:43 hannken 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 = config_cfdriver_attach(&dm_cd); 174 if (error) 175 break; 176 177 error = config_cfattach_attach(dm_cd.cd_name, &dm_ca); 178 if (error) { 179 aprint_error("%s: unable to register cfattach\n", 180 dm_cd.cd_name); 181 return error; 182 } 183 184 error = devsw_attach(dm_cd.cd_name, &dm_bdevsw, &bmajor, 185 &dm_cdevsw, &cmajor); 186 if (error == EEXIST) 187 error = 0; 188 if (error) { 189 config_cfattach_detach(dm_cd.cd_name, &dm_ca); 190 config_cfdriver_detach(&dm_cd); 191 break; 192 } 193 dm_doinit(); 194 break; 195 case MODULE_CMD_FINI: 196 /* 197 * Disable unloading of dm module if there are any devices 198 * defined in driver. This is probably too strong we need 199 * to disable auto-unload only if there is mounted dm device 200 * present. 201 */ 202 if (dm_dev_counter > 0) 203 return EBUSY; 204 /* race window here */ 205 206 error = dmdestroy(); 207 if (error) 208 break; 209 210 config_cfdriver_detach(&dm_cd); 211 212 devsw_detach(&dm_bdevsw, &dm_cdevsw); 213 break; 214 case MODULE_CMD_STAT: 215 return ENOTTY; 216 default: 217 return ENOTTY; 218 } 219 220 return error; 221 #else 222 return ENOTTY; 223 #endif 224 } 225 #endif /* _MODULE */ 226 227 /* 228 * dm_match: 229 * 230 * Autoconfiguration match function for pseudo-device glue. 231 */ 232 static int 233 dm_match(device_t parent, cfdata_t match, void *aux) 234 { 235 236 /* Pseudo-device; always present. */ 237 return 1; 238 } 239 240 /* 241 * dm_attach: 242 * 243 * Autoconfiguration attach function for pseudo-device glue. 244 */ 245 static void 246 dm_attach(device_t parent, device_t self, void *aux) 247 { 248 249 if (!pmf_device_register(self, NULL, NULL)) 250 aprint_error_dev(self, "couldn't establish power handler\n"); 251 } 252 253 /* 254 * dm_detach: 255 * 256 * Autoconfiguration detach function for pseudo-device glue. 257 * This routine is called by dm_ioctl::dm_dev_remove_ioctl and by autoconf to 258 * remove devices created in device-mapper. 259 */ 260 static int 261 dm_detach(device_t self, int flags) 262 { 263 bool busy; 264 dm_dev_t *dmv; 265 266 dmv = dm_dev_lookup(NULL, NULL, device_unit(self)); 267 mutex_enter(&dmv->diskp->dk_openlock); 268 busy = (dmv->diskp->dk_openmask != 0 && (flags & DETACH_FORCE) == 0); 269 mutex_exit(&dmv->diskp->dk_openlock); 270 dm_dev_unbusy(dmv); 271 if (busy) 272 return EBUSY; 273 274 pmf_device_deregister(self); 275 276 /* Detach device from global device list */ 277 if ((dmv = dm_dev_detach(self)) == NULL) 278 return ENOENT; 279 280 /* Destroy active table first. */ 281 dm_table_destroy(&dmv->table_head, DM_TABLE_ACTIVE); 282 283 /* Destroy inactive table if exits, too. */ 284 dm_table_destroy(&dmv->table_head, DM_TABLE_INACTIVE); 285 286 dm_table_head_destroy(&dmv->table_head); 287 288 /* Destroy disk device structure */ 289 disk_detach(dmv->diskp); 290 disk_destroy(dmv->diskp); 291 292 /* Destroy device */ 293 dm_dev_free(dmv); 294 295 /* Decrement device counter After removing device */ 296 atomic_dec_32(&dm_dev_counter); 297 298 return 0; 299 } 300 301 static void 302 dm_doinit(void) 303 { 304 305 dm_target_init(); 306 dm_dev_init(); 307 dm_pdev_init(); 308 } 309 310 /* attach routine */ 311 void 312 dmattach(int n) 313 { 314 int error; 315 316 error = config_cfattach_attach(dm_cd.cd_name, &dm_ca); 317 if (error) 318 aprint_error("%s: unable to register cfattach\n", 319 dm_cd.cd_name); 320 else 321 dm_doinit(); 322 } 323 324 #ifdef _MODULE 325 /* Destroy routine */ 326 static int 327 dmdestroy(void) 328 { 329 int error; 330 331 error = config_cfattach_detach(dm_cd.cd_name, &dm_ca); 332 if (error) 333 return error; 334 335 dm_dev_destroy(); 336 dm_pdev_destroy(); 337 dm_target_destroy(); 338 339 return 0; 340 } 341 #endif /* _MODULE */ 342 343 static int 344 dmopen(dev_t dev, int flags, int mode, struct lwp *l) 345 { 346 dm_dev_t *dmv; 347 struct disk *dk; 348 349 dmv = dm_dev_lookup(NULL, NULL, minor(dev)); 350 if (dmv) { 351 dk = dmv->diskp; 352 mutex_enter(&dk->dk_openlock); 353 switch (mode) { 354 case S_IFCHR: 355 dk->dk_copenmask |= 1; 356 break; 357 case S_IFBLK: 358 dk->dk_bopenmask |= 1; 359 break; 360 } 361 dk->dk_openmask = dk->dk_copenmask | dk->dk_bopenmask; 362 mutex_exit(&dk->dk_openlock); 363 dm_dev_unbusy(dmv); 364 } 365 366 aprint_debug("dm open routine called %" PRIu32 "\n", minor(dev)); 367 return 0; 368 } 369 370 static int 371 dmclose(dev_t dev, int flags, int mode, struct lwp *l) 372 { 373 dm_dev_t *dmv; 374 struct disk *dk; 375 376 aprint_debug("dm close routine called %" PRIu32 "\n", minor(dev)); 377 378 dmv = dm_dev_lookup(NULL, NULL, minor(dev)); 379 if (dmv) { 380 dk = dmv->diskp; 381 mutex_enter(&dk->dk_openlock); 382 switch (mode) { 383 case S_IFCHR: 384 dk->dk_copenmask &= ~1; 385 break; 386 case S_IFBLK: 387 dk->dk_bopenmask &= ~1; 388 break; 389 } 390 dk->dk_openmask = dk->dk_copenmask | dk->dk_bopenmask; 391 mutex_exit(&dk->dk_openlock); 392 dm_dev_unbusy(dmv); 393 } 394 return 0; 395 } 396 397 398 static int 399 dmioctl(dev_t dev, const u_long cmd, void *data, int flag, struct lwp *l) 400 { 401 int r; 402 prop_dictionary_t dm_dict_in; 403 404 aprint_debug("dmioctl called\n"); 405 KASSERT(data != NULL); 406 407 if ((r = disk_ioctl_switch(dev, cmd, data)) == ENOTTY) { 408 struct plistref *pref = (struct plistref *)data; 409 410 switch(cmd) { 411 case NETBSD_DM_IOCTL: 412 aprint_debug("dm NETBSD_DM_IOCTL called\n"); 413 break; 414 default: 415 aprint_debug("dm unknown ioctl called\n"); 416 return ENOTTY; 417 break; /* NOT REACHED */ 418 } 419 420 if ((r = prop_dictionary_copyin_ioctl(pref, cmd, &dm_dict_in)) 421 != 0) 422 return r; 423 424 if ((r = dm_check_version(dm_dict_in)) != 0) 425 goto cleanup_exit; 426 427 /* run ioctl routine */ 428 if ((r = dm_cmd_to_fun(dm_dict_in)) != 0) 429 goto cleanup_exit; 430 431 cleanup_exit: 432 r = prop_dictionary_copyout_ioctl(pref, cmd, dm_dict_in); 433 prop_object_release(dm_dict_in); 434 } 435 436 return r; 437 } 438 439 /* 440 * Translate command sent from libdevmapper to func. 441 */ 442 static int 443 dm_cmd_to_fun(prop_dictionary_t dm_dict) 444 { 445 int i, r; 446 prop_string_t command; 447 448 if ((command = prop_dictionary_get(dm_dict, DM_IOCTL_COMMAND)) == NULL) 449 return EINVAL; 450 451 for (i = 0; cmd_fn[i].cmd != NULL; i++) 452 if (prop_string_equals_string(command, cmd_fn[i].cmd)) 453 break; 454 455 if (!cmd_fn[i].allowed && 456 (r = kauth_authorize_system(kauth_cred_get(), 457 KAUTH_SYSTEM_DEVMAPPER, 0, NULL, NULL, NULL)) != 0) 458 return r; 459 460 if (cmd_fn[i].cmd == NULL) 461 return EINVAL; 462 463 aprint_debug("ioctl %s called %p\n", cmd_fn[i].cmd, cmd_fn[i].fn); 464 if (cmd_fn[i].fn == NULL) 465 return 0; 466 467 return cmd_fn[i].fn(dm_dict); 468 } 469 470 /* 471 * Check for disk specific ioctls. 472 */ 473 static int 474 disk_ioctl_switch(dev_t dev, unsigned long cmd, void *data) 475 { 476 dm_dev_t *dmv; 477 478 /* disk ioctls make sense only on block devices */ 479 if (minor(dev) == 0) 480 return ENOTTY; 481 482 switch(cmd) { 483 case DIOCGWEDGEINFO: 484 { 485 struct dkwedge_info *dkw = (void *) data; 486 487 if ((dmv = dm_dev_lookup(NULL, NULL, minor(dev))) == NULL) 488 return ENODEV; 489 490 aprint_debug("DIOCGWEDGEINFO ioctl called\n"); 491 492 strlcpy(dkw->dkw_devname, dmv->name, 16); 493 strlcpy(dkw->dkw_wname, dmv->name, DM_NAME_LEN); 494 strlcpy(dkw->dkw_parent, dmv->name, 16); 495 496 dkw->dkw_offset = 0; 497 dm_table_disksize(&dmv->table_head, &dkw->dkw_size, NULL); 498 strcpy(dkw->dkw_ptype, DKW_PTYPE_FFS); 499 500 dm_dev_unbusy(dmv); 501 break; 502 } 503 case DIOCGDISKINFO: 504 { 505 struct plistref *pref = (struct plistref *) data; 506 507 if ((dmv = dm_dev_lookup(NULL, NULL, minor(dev))) == NULL) 508 return ENODEV; 509 510 aprint_debug("DIOCGDISKINFO ioctl called\n"); 511 512 if (dmv->diskp->dk_info == NULL) { 513 dm_dev_unbusy(dmv); 514 return ENOTSUP; 515 } else 516 prop_dictionary_copyout_ioctl(pref, cmd, 517 dmv->diskp->dk_info); 518 519 dm_dev_unbusy(dmv); 520 break; 521 } 522 case DIOCCACHESYNC: 523 { 524 dm_table_entry_t *table_en; 525 dm_table_t *tbl; 526 527 if ((dmv = dm_dev_lookup(NULL, NULL, minor(dev))) == NULL) 528 return ENODEV; 529 530 aprint_debug("DIOCCACHESYNC ioctl called\n"); 531 532 /* Select active table */ 533 tbl = dm_table_get_entry(&dmv->table_head, DM_TABLE_ACTIVE); 534 535 /* 536 * Call sync target routine for all table entries. Target sync 537 * routine basically call DIOCCACHESYNC on underlying devices. 538 */ 539 SLIST_FOREACH(table_en, tbl, next) 540 if (table_en->target->sync) 541 table_en->target->sync(table_en); 542 dm_table_release(&dmv->table_head, DM_TABLE_ACTIVE); 543 dm_dev_unbusy(dmv); 544 break; 545 } 546 case DIOCGSECTORSIZE: 547 { 548 unsigned int secsize, *valp = data; 549 550 if ((dmv = dm_dev_lookup(NULL, NULL, minor(dev))) == NULL) 551 return ENODEV; 552 553 aprint_debug("DIOCGSECTORSIZE ioctl called\n"); 554 555 dm_table_disksize(&dmv->table_head, NULL, &secsize); 556 *valp = secsize; 557 558 dm_dev_unbusy(dmv); 559 break; 560 } 561 case DIOCGMEDIASIZE: 562 { 563 off_t *valp = data; 564 uint64_t numsec; 565 566 if ((dmv = dm_dev_lookup(NULL, NULL, minor(dev))) == NULL) 567 return ENODEV; 568 569 aprint_debug("DIOCGMEDIASIZE ioctl called\n"); 570 571 dm_table_disksize(&dmv->table_head, &numsec, NULL); 572 *valp = numsec; 573 574 dm_dev_unbusy(dmv); 575 break; 576 } 577 default: 578 aprint_debug("unknown disk_ioctl called\n"); 579 return ENOTTY; 580 break; /* NOT REACHED */ 581 } 582 583 return 0; 584 } 585 586 /* 587 * Do all IO operations on dm logical devices. 588 */ 589 static void 590 dmstrategy(struct buf *bp) 591 { 592 dm_dev_t *dmv; 593 dm_table_t *tbl; 594 dm_table_entry_t *table_en; 595 struct buf *nestbuf; 596 597 uint64_t buf_start, buf_len, issued_len; 598 uint64_t table_start, table_end; 599 uint64_t start, end; 600 601 buf_start = bp->b_blkno * DEV_BSIZE; 602 buf_len = bp->b_bcount; 603 604 table_end = 0; 605 issued_len = 0; 606 607 if ((dmv = dm_dev_lookup(NULL, NULL, minor(bp->b_dev))) == NULL) { 608 bp->b_error = EIO; 609 bp->b_resid = bp->b_bcount; 610 biodone(bp); 611 return; 612 } 613 614 if (bounds_check_with_mediasize(bp, DEV_BSIZE, 615 dm_table_size(&dmv->table_head)) <= 0) { 616 dm_dev_unbusy(dmv); 617 bp->b_resid = bp->b_bcount; 618 biodone(bp); 619 return; 620 } 621 622 /* 623 * disk(9) is part of device structure and it can't be used without 624 * mutual exclusion, use diskp_mtx until it will be fixed. 625 */ 626 mutex_enter(&dmv->diskp_mtx); 627 disk_busy(dmv->diskp); 628 mutex_exit(&dmv->diskp_mtx); 629 630 /* Select active table */ 631 tbl = dm_table_get_entry(&dmv->table_head, DM_TABLE_ACTIVE); 632 633 /* Nested buffers count down to zero therefore I have 634 to set bp->b_resid to maximal value. */ 635 bp->b_resid = bp->b_bcount; 636 637 /* 638 * Find out what tables I want to select. 639 */ 640 SLIST_FOREACH(table_en, tbl, next) { 641 /* I need number of bytes not blocks. */ 642 table_start = table_en->start * DEV_BSIZE; 643 /* 644 * I have to sub 1 from table_en->length to prevent 645 * off by one error 646 */ 647 table_end = table_start + table_en->length * DEV_BSIZE; 648 start = MAX(table_start, buf_start); 649 end = MIN(table_end, buf_start + buf_len); 650 651 aprint_debug("----------------------------------------\n"); 652 aprint_debug("table_start %010" PRIu64", table_end %010" 653 PRIu64 "\n", table_start, table_end); 654 aprint_debug("buf_start %010" PRIu64", buf_len %010" 655 PRIu64"\n", buf_start, buf_len); 656 aprint_debug("start-buf_start %010"PRIu64", end %010" 657 PRIu64"\n", start - buf_start, end); 658 aprint_debug("start %010" PRIu64", end %010" 659 PRIu64"\n", start, end); 660 aprint_debug("----------------------------------------\n"); 661 662 if (start < end) { 663 /* create nested buffer */ 664 nestbuf = getiobuf(NULL, true); 665 nestiobuf_setup(bp, nestbuf, start - buf_start, 666 end - start); 667 issued_len += end - start; 668 /* I need number of blocks. */ 669 nestbuf->b_blkno = (start - table_start) / DEV_BSIZE; 670 table_en->target->strategy(table_en, nestbuf); 671 } 672 } 673 674 if (issued_len < buf_len) 675 nestiobuf_done(bp, buf_len - issued_len, EINVAL); 676 677 mutex_enter(&dmv->diskp_mtx); 678 disk_unbusy(dmv->diskp, buf_len, bp ? (bp->b_flags & B_READ) : 0); 679 mutex_exit(&dmv->diskp_mtx); 680 681 dm_table_release(&dmv->table_head, DM_TABLE_ACTIVE); 682 dm_dev_unbusy(dmv); 683 } 684 685 686 static int 687 dmread(dev_t dev, struct uio *uio, int flag) 688 { 689 690 return physio(dmstrategy, NULL, dev, B_READ, dmminphys, uio); 691 } 692 693 static int 694 dmwrite(dev_t dev, struct uio *uio, int flag) 695 { 696 697 return physio(dmstrategy, NULL, dev, B_WRITE, dmminphys, uio); 698 } 699 700 static int 701 dmsize(dev_t dev) 702 { 703 dm_dev_t *dmv; 704 uint64_t size; 705 706 if ((dmv = dm_dev_lookup(NULL, NULL, minor(dev))) == NULL) 707 return -ENOENT; 708 709 size = dm_table_size(&dmv->table_head); 710 dm_dev_unbusy(dmv); 711 712 return size; 713 } 714 715 static void 716 dmminphys(struct buf *bp) 717 { 718 719 bp->b_bcount = MIN(bp->b_bcount, MAXPHYS); 720 } 721 722 void 723 dmgetproperties(struct disk *disk, dm_table_head_t *head) 724 { 725 uint64_t numsec; 726 unsigned int secsize; 727 struct disk_geom *dg; 728 729 dm_table_disksize(head, &numsec, &secsize); 730 731 dg = &disk->dk_geom; 732 733 memset(dg, 0, sizeof(*dg)); 734 dg->dg_secperunit = numsec; 735 dg->dg_secsize = secsize; 736 dg->dg_nsectors = 32; 737 dg->dg_ntracks = 64; 738 739 disk_set_info(NULL, disk, "ESDI"); 740 } 741 742 /* 743 * Transform char s to uint64_t offset number. 744 */ 745 uint64_t 746 atoi64(const char *s) 747 { 748 uint64_t n; 749 n = 0; 750 751 while (*s != '\0') { 752 if (!isdigit(*s)) 753 break; 754 755 n = (10 * n) + (*s - '0'); 756 s++; 757 } 758 759 return n; 760 } 761