1 /*- 2 * Copyright (c) 1994 Bruce D. Evans. 3 * All rights reserved. 4 * 5 * Copyright (c) 1990 The Regents of the University of California. 6 * All rights reserved. 7 * 8 * This code is derived from software contributed to Berkeley by 9 * William Jolitz. 10 * 11 * Copyright (c) 1982, 1986, 1988 Regents of the University of California. 12 * All rights reserved. 13 * 14 * Redistribution and use in source and binary forms, with or without 15 * modification, are permitted provided that the following conditions 16 * are met: 17 * 1. Redistributions of source code must retain the above copyright 18 * notice, this list of conditions and the following disclaimer. 19 * 2. Redistributions in binary form must reproduce the above copyright 20 * notice, this list of conditions and the following disclaimer in the 21 * documentation and/or other materials provided with the distribution. 22 * 3. Neither the name of the University nor the names of its contributors 23 * may be used to endorse or promote products derived from this software 24 * without specific prior written permission. 25 * 26 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 27 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 28 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 29 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 30 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 31 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 32 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 33 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 34 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 35 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 36 * SUCH DAMAGE. 37 * 38 * from: @(#)wd.c 7.2 (Berkeley) 5/9/91 39 * from: wd.c,v 1.55 1994/10/22 01:57:12 phk Exp $ 40 * from: @(#)ufs_disksubr.c 7.16 (Berkeley) 5/4/91 41 * from: ufs_disksubr.c,v 1.8 1994/06/07 01:21:39 phk Exp $ 42 * $FreeBSD: src/sys/kern/subr_diskslice.c,v 1.82.2.6 2001/07/24 09:49:41 dd Exp $ 43 */ 44 45 #include <sys/param.h> 46 #include <sys/systm.h> 47 #include <sys/buf.h> 48 #include <sys/conf.h> 49 #include <sys/disklabel.h> 50 #include <sys/disklabel32.h> 51 #include <sys/disklabel64.h> 52 #include <sys/diskslice.h> 53 #include <sys/disk.h> 54 #include <sys/diskmbr.h> 55 #include <sys/fcntl.h> 56 #include <sys/malloc.h> 57 #include <sys/stat.h> 58 #include <sys/syslog.h> 59 #include <sys/proc.h> 60 #include <sys/vnode.h> 61 #include <sys/device.h> 62 63 #include <vfs/ufs/dinode.h> /* XXX used only for fs.h */ 64 #include <vfs/ufs/fs.h> /* XXX used only to get BBSIZE/SBSIZE */ 65 #include <sys/devfs.h> 66 67 static int dsreadandsetlabel(cdev_t dev, u_int flags, 68 struct diskslices *ssp, struct diskslice *sp, 69 struct disk_info *info); 70 static void free_ds_label (struct diskslices *ssp, int slice); 71 static void set_ds_label (struct diskslices *ssp, int slice, disklabel_t lp, 72 disklabel_ops_t ops); 73 static void set_ds_wlabel (struct diskslices *ssp, int slice, int wlabel); 74 75 /* 76 * Determine the size of the transfer, and make sure it is 77 * within the boundaries of the partition. Adjust transfer 78 * if needed, and signal errors or early completion. 79 * 80 * XXX TODO: 81 * o Split buffers that are too big for the device. 82 * o Check for overflow. 83 * o Finish cleaning this up. 84 * 85 * This function returns 1 on success, 0 if transfer equates 86 * to EOF (end of disk) or -1 on failure. The appropriate 87 * 'errno' value is also set in bp->b_error and bp->b_flags 88 * is marked with B_ERROR. 89 */ 90 struct bio * 91 dscheck(cdev_t dev, struct bio *bio, struct diskslices *ssp) 92 { 93 struct buf *bp = bio->bio_buf; 94 struct bio *nbio; 95 disklabel_t lp; 96 disklabel_ops_t ops; 97 long nsec; 98 u_int64_t secno; 99 u_int64_t endsecno; 100 u_int64_t slicerel_secno; 101 struct diskslice *sp; 102 u_int32_t part; 103 u_int32_t slice; 104 int shift; 105 int mask; 106 107 slice = dkslice(dev); 108 part = dkpart(dev); 109 110 if (bio->bio_offset < 0) { 111 kprintf("dscheck(%s): negative bio_offset %lld\n", 112 devtoname(dev), (long long)bio->bio_offset); 113 goto bad; 114 } 115 if (slice >= ssp->dss_nslices) { 116 kprintf("dscheck(%s): slice too large %d/%d\n", 117 devtoname(dev), slice, ssp->dss_nslices); 118 goto bad; 119 } 120 sp = &ssp->dss_slices[slice]; 121 /* 122 * Calculate secno and nsec 123 */ 124 if (ssp->dss_secmult == 1) { 125 shift = DEV_BSHIFT; 126 goto doshift; 127 } else if (ssp->dss_secshift != -1) { 128 shift = DEV_BSHIFT + ssp->dss_secshift; 129 doshift: 130 mask = (1 << shift) - 1; 131 if ((int)bp->b_bcount & mask) 132 goto bad_bcount; 133 if ((int)bio->bio_offset & mask) 134 goto bad_blkno; 135 secno = bio->bio_offset >> shift; 136 nsec = bp->b_bcount >> shift; 137 } else { 138 if (bp->b_bcount % ssp->dss_secsize) 139 goto bad_bcount; 140 if (bio->bio_offset % ssp->dss_secsize) 141 goto bad_blkno; 142 secno = bio->bio_offset / ssp->dss_secsize; 143 nsec = bp->b_bcount / ssp->dss_secsize; 144 } 145 146 /* 147 * Calculate slice-relative sector number end slice-relative 148 * limit. 149 */ 150 if (slice == WHOLE_DISK_SLICE) { 151 /* 152 * Labels have not been allowed on whole-disks for a while. 153 * This really puts the nail in the coffin. 154 * 155 * Accesses to the WHOLE_DISK_SLICE do not use a disklabel 156 * and partition numbers are special-cased. Currently numbers 157 * less then 128 are not allowed. Partition numbers >= 128 158 * are encoded in the high 8 bits of the 64 bit buffer offset 159 * and are fed directly through to the device with no 160 * further interpretation. In particular, no sector 161 * translation interpretation should occur because the 162 * sector size for the special raw access may not be the 163 * same as the nominal sector size for the device. 164 */ 165 lp.opaque = NULL; 166 if (part < 128) { 167 kprintf("dscheck(%s): illegal partition number (%d) " 168 "for WHOLE_DISK_SLICE access\n", 169 devtoname(dev), part); 170 goto bad; 171 } else if (part != WHOLE_SLICE_PART) { 172 nbio = push_bio(bio); 173 nbio->bio_offset = bio->bio_offset | 174 (u_int64_t)part << 56; 175 return(nbio); 176 } else { 177 /* 178 * If writing to the raw disk request a 179 * reprobe on the last close. 180 */ 181 if (bp->b_cmd == BUF_CMD_WRITE) 182 sp->ds_flags |= DSF_REPROBE; 183 } 184 185 /* 186 * sp->ds_size is for the whole disk in the WHOLE_DISK_SLICE, 187 * there are no reserved areas. 188 */ 189 endsecno = sp->ds_size; 190 slicerel_secno = secno; 191 } else if (part == WHOLE_SLICE_PART) { 192 /* 193 * NOTE! opens on a whole-slice partition will not attempt 194 * to read a disklabel in, so there may not be an in-core 195 * disklabel even if there is one on the disk. 196 */ 197 endsecno = sp->ds_size; 198 slicerel_secno = secno; 199 } else if ((lp = sp->ds_label).opaque != NULL) { 200 /* 201 * A label is present, extract the partition. Snooping of 202 * the disklabel is not supported even if accessible. Of 203 * course, the reserved area is still write protected. 204 */ 205 ops = sp->ds_ops; 206 if (ops->op_getpartbounds(ssp, lp, part, 207 &slicerel_secno, &endsecno)) { 208 kprintf("dscheck(%s): partition %d out of bounds\n", 209 devtoname(dev), part); 210 goto bad; 211 } 212 slicerel_secno += secno; 213 } else { 214 /* 215 * Attempt to access partition when no disklabel present 216 */ 217 kprintf("dscheck(%s): attempt to access non-existent partition\n", 218 devtoname(dev)); 219 goto bad; 220 } 221 222 /* 223 * Disallow writes to reserved areas unless ds_wlabel allows it. 224 * If the reserved area is written to request a reprobe of the 225 * disklabel when the slice is closed. 226 */ 227 if (slicerel_secno < sp->ds_reserved && nsec && 228 bp->b_cmd == BUF_CMD_WRITE) { 229 if (sp->ds_wlabel == 0) { 230 bp->b_error = EROFS; 231 goto error; 232 } 233 sp->ds_flags |= DSF_REPROBE; 234 } 235 236 /* 237 * If we get here, bio_offset must be on a block boundary and 238 * the sector size must be a power of 2. 239 */ 240 if ((bio->bio_offset & (ssp->dss_secsize - 1)) || 241 (ssp->dss_secsize ^ (ssp->dss_secsize - 1)) != 242 ((ssp->dss_secsize << 1) - 1)) { 243 kprintf("%s: invalid BIO offset, not sector aligned or" 244 " invalid sector size (not power of 2) %08llx %d\n", 245 devtoname(dev), (long long)bio->bio_offset, 246 ssp->dss_secsize); 247 goto bad; 248 } 249 250 /* 251 * EOF handling 252 */ 253 if (secno + nsec > endsecno) { 254 /* 255 * Return an error if beyond the end of the disk, or 256 * if B_BNOCLIP is set. Tell the system that we do not 257 * need to keep the buffer around. 258 */ 259 if (secno > endsecno || (bp->b_flags & B_BNOCLIP)) 260 goto bad; 261 262 /* 263 * If exactly at end of disk, return an EOF. Throw away 264 * the buffer contents, if any, by setting B_INVAL. 265 */ 266 if (secno == endsecno) { 267 bp->b_resid = bp->b_bcount; 268 bp->b_flags |= B_INVAL; 269 goto done; 270 } 271 272 /* 273 * Else truncate 274 */ 275 nsec = endsecno - secno; 276 bp->b_bcount = nsec * ssp->dss_secsize; 277 } 278 279 nbio = push_bio(bio); 280 nbio->bio_offset = (off_t)(sp->ds_offset + slicerel_secno) * 281 ssp->dss_secsize; 282 return (nbio); 283 284 bad_bcount: 285 kprintf( 286 "dscheck(%s): b_bcount %d is not on a sector boundary (ssize %d)\n", 287 devtoname(dev), bp->b_bcount, ssp->dss_secsize); 288 goto bad; 289 290 bad_blkno: 291 kprintf( 292 "dscheck(%s): bio_offset %lld is not on a sector boundary (ssize %d)\n", 293 devtoname(dev), (long long)bio->bio_offset, ssp->dss_secsize); 294 bad: 295 bp->b_error = EINVAL; 296 /* fall through */ 297 error: 298 /* 299 * Terminate the I/O with a ranging error. Since the buffer is 300 * either illegal or beyond the file EOF, mark it B_INVAL as well. 301 */ 302 bp->b_resid = bp->b_bcount; 303 bp->b_flags |= B_ERROR | B_INVAL; 304 done: 305 /* 306 * Caller must biodone() the originally passed bio if NULL is 307 * returned. 308 */ 309 return (NULL); 310 } 311 312 /* 313 * dsclose() - close a cooked disk slice. 314 * 315 * WARNING! The passed diskslices and related diskslice structures may 316 * be invalidated or replaced by this function, callers must 317 * reload from the disk structure for continued access. 318 */ 319 void 320 dsclose(cdev_t dev, int mode, struct diskslices *ssp) 321 { 322 u_int32_t part; 323 u_int32_t slice; 324 struct diskslice *sp; 325 326 slice = dkslice(dev); 327 part = dkpart(dev); 328 if (slice < ssp->dss_nslices) { 329 sp = &ssp->dss_slices[slice]; 330 dsclrmask(sp, part); 331 if (sp->ds_flags & DSF_REPROBE) { 332 sp->ds_flags &= ~DSF_REPROBE; 333 if (slice == WHOLE_DISK_SLICE) { 334 disk_msg_send_sync(DISK_DISK_REPROBE, 335 dev->si_disk, NULL); 336 devfs_config(); 337 } else { 338 disk_msg_send_sync(DISK_SLICE_REPROBE, 339 dev->si_disk, sp); 340 devfs_config(); 341 } 342 /* ssp and sp may both be invalid after reprobe */ 343 } 344 } 345 } 346 347 void 348 dsgone(struct diskslices **sspp) 349 { 350 int slice; 351 struct diskslices *ssp; 352 353 if ((ssp = *sspp) != NULL) { 354 for (slice = 0; slice < ssp->dss_nslices; slice++) 355 free_ds_label(ssp, slice); 356 kfree(ssp, M_DEVBUF); 357 *sspp = NULL; 358 } 359 } 360 361 /* 362 * For the "write" commands (DIOCSDINFO and DIOCWDINFO), this 363 * is subject to the same restriction as dsopen(). 364 */ 365 int 366 dsioctl(cdev_t dev, u_long cmd, caddr_t data, int flags, 367 struct diskslices **sspp, struct disk_info *info) 368 { 369 int error; 370 disklabel_t lp; 371 disklabel_t lptmp; 372 disklabel_ops_t ops; 373 int old_wlabel; 374 u_int32_t openmask[DKMAXPARTITIONS/(sizeof(u_int32_t)*8)]; 375 int part; 376 int slice; 377 struct diskslice *sp; 378 struct diskslices *ssp; 379 380 slice = dkslice(dev); 381 part = dkpart(dev); 382 ssp = *sspp; 383 if (ssp == NULL) 384 return (EINVAL); 385 if (slice >= ssp->dss_nslices) 386 return (EINVAL); 387 sp = &ssp->dss_slices[slice]; 388 lp = sp->ds_label; 389 ops = sp->ds_ops; /* may be NULL if no label */ 390 391 switch (cmd) { 392 case DIOCGDVIRGIN32: 393 ops = &disklabel32_ops; 394 /* fall through */ 395 case DIOCGDVIRGIN64: 396 if (cmd != DIOCGDVIRGIN32) 397 ops = &disklabel64_ops; 398 /* 399 * You can only retrieve a virgin disklabel on the whole 400 * disk slice or whole-slice partition. 401 */ 402 if (slice != WHOLE_DISK_SLICE && 403 part != WHOLE_SLICE_PART) { 404 return(EINVAL); 405 } 406 407 lp.opaque = data; 408 ops->op_makevirginlabel(lp, ssp, sp, info); 409 return (0); 410 411 case DIOCGDINFO32: 412 case DIOCGDINFO64: 413 /* 414 * You can only retrieve a disklabel on the whole 415 * slice partition. 416 * 417 * We do not support labels directly on whole-disks 418 * any more (that is, disks without slices), unless the 419 * device driver has asked for a compatible label (e.g. 420 * for a CD) to allow booting off of storage that is 421 * otherwise unlabeled. 422 */ 423 error = 0; 424 if (part != WHOLE_SLICE_PART) 425 return(EINVAL); 426 if (slice == WHOLE_DISK_SLICE && 427 (info->d_dsflags & DSO_COMPATLABEL) == 0) { 428 return (ENODEV); 429 } 430 if (sp->ds_label.opaque == NULL) { 431 error = dsreadandsetlabel(dev, info->d_dsflags, 432 ssp, sp, info); 433 ops = sp->ds_ops; /* may be NULL */ 434 } 435 436 /* 437 * The type of label we found must match the type of 438 * label requested. 439 */ 440 if (error == 0 && IOCPARM_LEN(cmd) != ops->labelsize) 441 error = ENOATTR; 442 if (error == 0) 443 bcopy(sp->ds_label.opaque, data, ops->labelsize); 444 return (error); 445 446 case DIOCGMEDIASIZE: 447 /* 448 * The disk management layer may not have read the 449 * disklabel yet because simply opening a slice no 450 * longer 'probes' the disk that way. Be sure we 451 * have tried. 452 * 453 * We ignore any error. 454 */ 455 if (sp->ds_label.opaque == NULL && 456 part == WHOLE_SLICE_PART && 457 slice != WHOLE_DISK_SLICE) { 458 dsreadandsetlabel(dev, info->d_dsflags, 459 ssp, sp, info); 460 ops = sp->ds_ops; /* may be NULL */ 461 } 462 463 *(off_t *)data = 0; 464 465 if (slice != WHOLE_DISK_SLICE && part != WHOLE_SLICE_PART) { 466 u_int64_t start; 467 u_int64_t blocks; 468 469 if (lp.opaque == NULL) 470 return(EINVAL); 471 if (ops->op_getpartbounds(ssp, lp, part, 472 &start, &blocks)) { 473 return(EINVAL); 474 } 475 *(off_t *)data = blocks * info->d_media_blksize; 476 } else { 477 *(off_t *)data = (u_int64_t)sp->ds_size * 478 info->d_media_blksize; 479 } 480 481 return 0; 482 483 case DIOCGSECTORSIZE: 484 *(u_int *)data = info->d_media_blksize; 485 return 0; 486 487 case DIOCGPART: 488 { 489 struct partinfo *dpart = (void *)data; 490 491 /* 492 * The disk management layer may not have read the 493 * disklabel yet because simply opening a slice no 494 * longer 'probes' the disk that way. Be sure we 495 * have tried. 496 * 497 * We ignore any error. 498 */ 499 if (sp->ds_label.opaque == NULL && 500 part == WHOLE_SLICE_PART && 501 slice != WHOLE_DISK_SLICE) { 502 dsreadandsetlabel(dev, info->d_dsflags, 503 ssp, sp, info); 504 ops = sp->ds_ops; /* may be NULL */ 505 } 506 507 bzero(dpart, sizeof(*dpart)); 508 dpart->media_offset = (u_int64_t)sp->ds_offset * 509 info->d_media_blksize; 510 dpart->media_size = (u_int64_t)sp->ds_size * 511 info->d_media_blksize; 512 dpart->media_blocks = sp->ds_size; 513 dpart->media_blksize = info->d_media_blksize; 514 dpart->reserved_blocks= sp->ds_reserved; 515 dpart->fstype_uuid = sp->ds_type_uuid; 516 dpart->storage_uuid = sp->ds_stor_uuid; 517 518 if (slice != WHOLE_DISK_SLICE && 519 part != WHOLE_SLICE_PART) { 520 u_int64_t start; 521 u_int64_t blocks; 522 if (lp.opaque == NULL) 523 return(EINVAL); 524 if (ops->op_getpartbounds(ssp, lp, part, 525 &start, &blocks)) { 526 return(EINVAL); 527 } 528 ops->op_loadpartinfo(lp, part, dpart); 529 dpart->media_offset += start * 530 info->d_media_blksize; 531 dpart->media_size = blocks * 532 info->d_media_blksize; 533 dpart->media_blocks = blocks; 534 535 /* 536 * partition starting sector (p_offset) 537 * requires slice's reserved areas to be 538 * adjusted. 539 */ 540 if (dpart->reserved_blocks > start) 541 dpart->reserved_blocks -= start; 542 else 543 dpart->reserved_blocks = 0; 544 } 545 546 /* 547 * Load remaining fields from the info structure 548 */ 549 dpart->d_nheads = info->d_nheads; 550 dpart->d_ncylinders = info->d_ncylinders; 551 dpart->d_secpertrack = info->d_secpertrack; 552 dpart->d_secpercyl = info->d_secpercyl; 553 } 554 return (0); 555 556 case DIOCGSLICEINFO: 557 bcopy(ssp, data, (char *)&ssp->dss_slices[ssp->dss_nslices] - 558 (char *)ssp); 559 return (0); 560 561 case DIOCSDINFO32: 562 ops = &disklabel32_ops; 563 /* fall through */ 564 case DIOCSDINFO64: 565 if (cmd != DIOCSDINFO32) 566 ops = &disklabel64_ops; 567 /* 568 * You can write a disklabel on the whole disk slice or 569 * whole-slice partition. 570 */ 571 if (slice != WHOLE_DISK_SLICE && 572 part != WHOLE_SLICE_PART) { 573 return(EINVAL); 574 } 575 576 /* 577 * We no longer support writing disklabels directly to media 578 * without there being a slice. Keep this as a separate 579 * conditional. 580 */ 581 if (slice == WHOLE_DISK_SLICE) 582 return (ENODEV); 583 if (!(flags & FWRITE)) 584 return (EBADF); 585 586 /* 587 * If an existing label is present it must be the same 588 * type as the label being passed by the ioctl. 589 */ 590 if (sp->ds_label.opaque && sp->ds_ops != ops) 591 return (ENOATTR); 592 593 /* 594 * Create a temporary copy of the existing label 595 * (if present) so setdisklabel can compare it against 596 * the new label. 597 */ 598 lp.opaque = kmalloc(ops->labelsize, M_DEVBUF, M_WAITOK); 599 if (sp->ds_label.opaque == NULL) 600 bzero(lp.opaque, ops->labelsize); 601 else 602 bcopy(sp->ds_label.opaque, lp.opaque, ops->labelsize); 603 if (sp->ds_label.opaque == NULL) { 604 bzero(openmask, sizeof(openmask)); 605 } else { 606 bcopy(sp->ds_openmask, openmask, sizeof(openmask)); 607 } 608 lptmp.opaque = data; 609 error = ops->op_setdisklabel(lp, lptmp, ssp, sp, openmask); 610 disk_msg_send_sync(DISK_SLICE_REPROBE, dev->si_disk, sp); 611 devfs_config(); 612 if (error != 0) { 613 kfree(lp.opaque, M_DEVBUF); 614 return (error); 615 } 616 free_ds_label(ssp, slice); 617 set_ds_label(ssp, slice, lp, ops); 618 return (0); 619 620 case DIOCSYNCSLICEINFO: 621 /* 622 * This ioctl can only be done on the whole disk 623 */ 624 if (slice != WHOLE_DISK_SLICE || part != WHOLE_SLICE_PART) 625 return (EINVAL); 626 627 if (*(int *)data == 0) { 628 for (slice = 0; slice < ssp->dss_nslices; slice++) { 629 struct diskslice *ds = &ssp->dss_slices[slice]; 630 631 switch(dscountmask(ds)) { 632 case 0: 633 break; 634 case 1: 635 if (slice != WHOLE_DISK_SLICE) 636 return (EBUSY); 637 if (!dschkmask(ds, RAW_PART)) 638 return (EBUSY); 639 break; 640 default: 641 return (EBUSY); 642 } 643 } 644 } 645 646 disk_msg_send_sync(DISK_DISK_REPROBE, dev->si_disk, NULL); 647 devfs_config(); 648 return 0; 649 650 case DIOCWDINFO32: 651 case DIOCWDINFO64: 652 error = dsioctl(dev, ((cmd == DIOCWDINFO32) ? 653 DIOCSDINFO32 : DIOCSDINFO64), 654 data, flags, &ssp, info); 655 if (error == 0 && sp->ds_label.opaque == NULL) 656 error = EINVAL; 657 if (part != WHOLE_SLICE_PART) 658 error = EINVAL; 659 if (error != 0) 660 return (error); 661 662 /* 663 * Allow the reserved area to be written, reload ops 664 * because the DIOCSDINFO op above may have installed 665 * a new label type. 666 */ 667 ops = sp->ds_ops; 668 old_wlabel = sp->ds_wlabel; 669 set_ds_wlabel(ssp, slice, TRUE); 670 error = ops->op_writedisklabel(dev, ssp, sp, sp->ds_label); 671 disk_msg_send_sync(DISK_SLICE_REPROBE, dev->si_disk, sp); 672 devfs_config(); 673 set_ds_wlabel(ssp, slice, old_wlabel); 674 /* XXX should invalidate in-core label if write failed. */ 675 return (error); 676 677 case DIOCWLABEL: 678 if (slice == WHOLE_DISK_SLICE) 679 return (ENODEV); 680 if (!(flags & FWRITE)) 681 return (EBADF); 682 set_ds_wlabel(ssp, slice, *(int *)data != 0); 683 return (0); 684 685 default: 686 return (ENOIOCTL); 687 } 688 } 689 690 int 691 dsisopen(struct diskslices *ssp) 692 { 693 int slice; 694 695 if (ssp == NULL) 696 return (0); 697 for (slice = 0; slice < ssp->dss_nslices; slice++) { 698 if (dscountmask(&ssp->dss_slices[slice])) 699 return (1); 700 } 701 return (0); 702 } 703 704 /* 705 * Allocate a slices "struct" and initialize it to contain only an empty 706 * compatibility slice (pointing to itself), a whole disk slice (covering 707 * the disk as described by the label), and (nslices - BASE_SLICES) empty 708 * slices beginning at BASE_SLICE. 709 * 710 * Note that the compatibility slice is no longer really a compatibility 711 * slice. It is slice 0 if a GPT label is present, and the dangerously 712 * dedicated slice if no slice table otherwise exists. Else it is 0-sized. 713 */ 714 struct diskslices * 715 dsmakeslicestruct(int nslices, struct disk_info *info) 716 { 717 struct diskslice *sp; 718 struct diskslices *ssp; 719 720 ssp = kmalloc(offsetof(struct diskslices, dss_slices) + 721 nslices * sizeof *sp, M_DEVBUF, M_WAITOK); 722 ssp->dss_first_bsd_slice = COMPATIBILITY_SLICE; 723 ssp->dss_nslices = nslices; 724 ssp->dss_oflags = 0; 725 726 /* 727 * Figure out if we can use shifts or whether we have to 728 * use mod/multply to translate byte offsets into sector numbers. 729 */ 730 if ((info->d_media_blksize ^ (info->d_media_blksize - 1)) == 731 (info->d_media_blksize << 1) - 1) { 732 ssp->dss_secmult = info->d_media_blksize / DEV_BSIZE; 733 if (ssp->dss_secmult & (ssp->dss_secmult - 1)) 734 ssp->dss_secshift = -1; 735 else 736 ssp->dss_secshift = ffs(ssp->dss_secmult) - 1; 737 } else { 738 ssp->dss_secmult = 0; 739 ssp->dss_secshift = -1; 740 } 741 ssp->dss_secsize = info->d_media_blksize; 742 sp = &ssp->dss_slices[0]; 743 bzero(sp, nslices * sizeof *sp); 744 sp[WHOLE_DISK_SLICE].ds_size = info->d_media_blocks; 745 return (ssp); 746 } 747 748 char * 749 dsname(cdev_t dev, int unit, int slice, int part, char *partname) 750 { 751 return dev->si_name; 752 } 753 754 /* 755 * This should only be called when the unit is inactive and the strategy 756 * routine should not allow it to become active unless we call it. Our 757 * strategy routine must be special to allow activity. 758 */ 759 int 760 dsopen(cdev_t dev, int mode, u_int flags, 761 struct diskslices **sspp, struct disk_info *info) 762 { 763 struct diskslice *sp; 764 struct diskslices *ssp; 765 int slice; 766 int part; 767 768 ssp = *sspp; 769 dev->si_bsize_phys = info->d_media_blksize; 770 slice = dkslice(dev); 771 part = dkpart(dev); 772 sp = &ssp->dss_slices[slice]; 773 dssetmask(sp, part); 774 775 return 0; 776 } 777 778 /* 779 * Attempt to read the disklabel. If successful, store it in sp->ds_label. 780 * 781 * If we cannot read the disklabel and DSO_COMPATLABEL is set, we construct 782 * a fake label covering the whole disk. 783 */ 784 static 785 int 786 dsreadandsetlabel(cdev_t dev, u_int flags, 787 struct diskslices *ssp, struct diskslice *sp, 788 struct disk_info *info) 789 { 790 disklabel_t lp; 791 disklabel_ops_t ops; 792 const char *msg; 793 const char *sname; 794 char partname[2]; 795 int slice = dkslice(dev); 796 797 /* 798 * Probe the disklabel 799 */ 800 lp.opaque = NULL; 801 sname = dsname(dev, dkunit(dev), slice, WHOLE_SLICE_PART, partname); 802 ops = &disklabel32_ops; 803 msg = ops->op_readdisklabel(dev, sp, &lp, info); 804 if (msg && strcmp(msg, "no disk label") == 0) { 805 ops = &disklabel64_ops; 806 msg = disklabel64_ops.op_readdisklabel(dev, sp, &lp, info); 807 } 808 809 /* 810 * If we failed and COMPATLABEL is set, create a dummy disklabel. 811 */ 812 if (msg != NULL && (flags & DSO_COMPATLABEL)) { 813 msg = NULL; 814 if (sp->ds_size >= 0x100000000ULL) 815 ops = &disklabel64_ops; 816 else 817 ops = &disklabel32_ops; 818 lp = ops->op_clone_label(info, sp); 819 } 820 if (msg != NULL) { 821 if (sp->ds_type == DOSPTYP_386BSD || 822 sp->ds_type == DOSPTYP_DFLYBSD) { 823 log(LOG_WARNING, "%s: cannot find label (%s)\n", 824 sname, msg); 825 } 826 if (lp.opaque) 827 kfree(lp.opaque, M_DEVBUF); 828 } else { 829 set_ds_label(ssp, slice, lp, ops); 830 set_ds_wlabel(ssp, slice, FALSE); 831 } 832 return (msg ? EINVAL : 0); 833 } 834 835 int64_t 836 dssize(cdev_t dev, struct diskslices **sspp) 837 { 838 disklabel_t lp; 839 disklabel_ops_t ops; 840 int part; 841 int slice; 842 struct diskslices *ssp; 843 u_int64_t start; 844 u_int64_t blocks; 845 846 slice = dkslice(dev); 847 part = dkpart(dev); 848 ssp = *sspp; 849 if (ssp == NULL || slice >= ssp->dss_nslices 850 || !dschkmask(&ssp->dss_slices[slice], part)) { 851 if (dev_dopen(dev, FREAD, S_IFCHR, 852 proc0.p_ucred, NULL, NULL) != 0) 853 { 854 return (-1); 855 } 856 dev_dclose(dev, FREAD, S_IFCHR, NULL); 857 ssp = *sspp; 858 } 859 lp = ssp->dss_slices[slice].ds_label; 860 if (part == WHOLE_SLICE_PART) { 861 blocks = ssp->dss_slices[slice].ds_size; 862 } else if (lp.opaque == NULL) { 863 blocks = (u_int64_t)-1; 864 } else { 865 ops = ssp->dss_slices[slice].ds_ops; 866 if (ops->op_getpartbounds(ssp, lp, part, &start, &blocks)) 867 return (-1); 868 } 869 return ((int64_t)blocks); 870 } 871 872 static void 873 free_ds_label(struct diskslices *ssp, int slice) 874 { 875 struct diskslice *sp; 876 disklabel_t lp; 877 878 sp = &ssp->dss_slices[slice]; 879 lp = sp->ds_label; 880 if (lp.opaque != NULL) { 881 kfree(lp.opaque, M_DEVBUF); 882 lp.opaque = NULL; 883 set_ds_label(ssp, slice, lp, NULL); 884 } 885 } 886 887 static void 888 set_ds_label(struct diskslices *ssp, int slice, 889 disklabel_t lp, disklabel_ops_t ops) 890 { 891 struct diskslice *sp = &ssp->dss_slices[slice]; 892 893 sp->ds_label = lp; 894 sp->ds_ops = ops; 895 if (lp.opaque && slice != WHOLE_DISK_SLICE) 896 ops->op_adjust_label_reserved(ssp, slice, sp); 897 else 898 sp->ds_reserved = 0; 899 } 900 901 static void 902 set_ds_wlabel(struct diskslices *ssp, int slice, int wlabel) 903 { 904 ssp->dss_slices[slice].ds_wlabel = wlabel; 905 } 906 907