1 /* $NetBSD: subr_disk_mbr.c,v 1.35 2009/01/28 15:26:28 reinoud Exp $ */ 2 3 /* 4 * Copyright (c) 1982, 1986, 1988 Regents of the University of California. 5 * All rights reserved. 6 * 7 * Redistribution and use in source and binary forms, with or without 8 * modification, are permitted provided that the following conditions 9 * are met: 10 * 1. Redistributions of source code must retain the above copyright 11 * notice, this list of conditions and the following disclaimer. 12 * 2. Redistributions in binary form must reproduce the above copyright 13 * notice, this list of conditions and the following disclaimer in the 14 * documentation and/or other materials provided with the distribution. 15 * 3. Neither the name of the University nor the names of its contributors 16 * may be used to endorse or promote products derived from this software 17 * without specific prior written permission. 18 * 19 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 20 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 21 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 22 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 23 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 24 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 25 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 26 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 27 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 28 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 29 * SUCH DAMAGE. 30 * 31 * @(#)ufs_disksubr.c 7.16 (Berkeley) 5/4/91 32 */ 33 34 /* 35 * Code to find a NetBSD label on a disk that contains an i386 style MBR. 36 * The first NetBSD label found in the 2nd sector of a NetBSD partition 37 * is used. 38 * If we don't find a label searching the MBR, we look at the start of the 39 * disk, if that fails then a label is faked up from the MBR. 40 * 41 * If there isn't a disklabel or anything in the MBR then the disc is searched 42 * for ecma-167/iso9660/udf style partition indicators. 43 * Useful for media or files that contain single filesystems (etc). 44 * 45 * This code will read host endian netbsd labels from little endian MBR. 46 * 47 * Based on the i386 disksubr.c 48 * 49 * Since the mbr only has 32bit fields for sector addresses, we do the same. 50 * 51 * XXX There are potential problems writing labels to disks where there 52 * is only space for 8 netbsd partitions but this code has been compiled 53 * with MAXPARTITIONS=16. 54 */ 55 56 #include <sys/cdefs.h> 57 __KERNEL_RCSID(0, "$NetBSD: subr_disk_mbr.c,v 1.35 2009/01/28 15:26:28 reinoud Exp $"); 58 59 #include <sys/param.h> 60 #include <sys/systm.h> 61 #include <sys/buf.h> 62 #include <sys/bootblock.h> 63 #include <sys/disklabel.h> 64 #include <sys/disk.h> 65 #include <sys/syslog.h> 66 #include <sys/vnode.h> 67 #include <sys/fcntl.h> 68 #include <sys/conf.h> 69 #include <sys/cdio.h> 70 #include <fs/udf/ecma167-udf.h> 71 72 #include <sys/kauth.h> 73 74 #include "opt_mbr.h" 75 76 typedef struct mbr_partition mbr_partition_t; 77 78 /* 79 * We allocate a buffer 2 sectors large, and look in both.... 80 * That means we find labels written by other ports with different offsets. 81 * LABELSECTOR and LABELOFFSET are only used if the disk doesn't have a label. 82 */ 83 #if LABELSECTOR > 1 || LABELOFFSET > 512 84 #error Invalid LABELSECTOR or LABELOFFSET 85 #endif 86 87 #define MBR_LABELSECTOR 1 88 89 #define SCAN_CONTINUE 0 90 #define SCAN_FOUND 1 91 #define SCAN_ERROR 2 92 93 typedef struct mbr_args { 94 struct disklabel *lp; 95 void (*strat)(struct buf *); 96 struct buf *bp; 97 const char *msg; 98 int error; 99 int written; /* number of times we wrote label */ 100 int found_mbr; /* set if disk has a valid mbr */ 101 uint label_sector; /* where we found the label */ 102 int action; 103 uint32_t secperunit; 104 #define READ_LABEL 1 105 #define UPDATE_LABEL 2 106 #define WRITE_LABEL 3 107 } mbr_args_t; 108 109 static int validate_label(mbr_args_t *, uint); 110 static int look_netbsd_part(mbr_args_t *, mbr_partition_t *, int, uint); 111 static int write_netbsd_label(mbr_args_t *, mbr_partition_t *, int, uint); 112 113 static int 114 read_sector(mbr_args_t *a, uint sector, int count) 115 { 116 int error; 117 118 error = disk_read_sectors(a->strat, a->lp, a->bp, sector, count); 119 if (error != 0) 120 a->error = error; 121 return error; 122 } 123 124 /* 125 * Scan MBR for partitions, call 'action' routine for each. 126 */ 127 128 static int 129 scan_mbr(mbr_args_t *a, int (*actn)(mbr_args_t *, mbr_partition_t *, int, uint)) 130 { 131 mbr_partition_t ptns[MBR_PART_COUNT]; 132 mbr_partition_t *dp; 133 struct mbr_sector *mbr; 134 uint ext_base, this_ext, next_ext; 135 int rval; 136 int i; 137 int j; 138 #ifdef COMPAT_386BSD_MBRPART 139 int dp_386bsd = -1; 140 int ap_386bsd = -1; 141 #endif 142 143 ext_base = 0; 144 this_ext = 0; 145 for (;;) { 146 if (read_sector(a, this_ext, 1)) { 147 a->msg = "dos partition I/O error"; 148 return SCAN_ERROR; 149 } 150 151 /* Note: Magic number is little-endian. */ 152 mbr = (void *)a->bp->b_data; 153 if (mbr->mbr_magic != htole16(MBR_MAGIC)) 154 return SCAN_CONTINUE; 155 156 /* Copy data out of buffer so action can use bp */ 157 memcpy(ptns, &mbr->mbr_parts, sizeof ptns); 158 159 /* Look for drivers and skip them */ 160 if (ext_base == 0 && ptns[0].mbrp_type == MBR_PTYPE_DM6_DDO) { 161 /* We've found a DM6 DDO partition type (used by 162 * the Ontrack Disk Manager drivers). 163 * 164 * Ensure that there are no other partitions in the 165 * MBR and jump to the real partition table (stored 166 * in the first sector of the second track). */ 167 bool ok = true; 168 169 for (i = 1; i < MBR_PART_COUNT; i++) 170 if (ptns[i].mbrp_type != MBR_PTYPE_UNUSED) 171 ok = false; 172 173 if (ok) { 174 this_ext = le32toh(a->lp->d_secpercyl / 175 a->lp->d_ntracks); 176 continue; 177 } 178 } 179 180 /* look for NetBSD partition */ 181 next_ext = 0; 182 dp = ptns; 183 j = 0; 184 for (i = 0; i < MBR_PART_COUNT; i++, dp++) { 185 if (dp->mbrp_type == MBR_PTYPE_UNUSED) 186 continue; 187 /* Check end of partition is inside disk limits */ 188 if ((uint64_t)ext_base + le32toh(dp->mbrp_start) + 189 le32toh(dp->mbrp_size) > a->lp->d_secperunit) { 190 /* This mbr doesn't look good.... */ 191 a->msg = "mbr partition exceeds disk size"; 192 /* ...but don't report this as an error (yet) */ 193 return SCAN_CONTINUE; 194 } 195 a->found_mbr = 1; 196 if (MBR_IS_EXTENDED(dp->mbrp_type)) { 197 next_ext = le32toh(dp->mbrp_start); 198 continue; 199 } 200 #ifdef COMPAT_386BSD_MBRPART 201 if (dp->mbrp_type == MBR_PTYPE_386BSD) { 202 /* 203 * If more than one matches, take last, 204 * as NetBSD install tool does. 205 */ 206 if (this_ext == 0) { 207 dp_386bsd = i; 208 ap_386bsd = j; 209 } 210 continue; 211 } 212 #endif 213 rval = (*actn)(a, dp, j, this_ext); 214 if (rval != SCAN_CONTINUE) 215 return rval; 216 j++; 217 } 218 if (next_ext == 0) 219 break; 220 if (ext_base == 0) { 221 ext_base = next_ext; 222 next_ext = 0; 223 } 224 next_ext += ext_base; 225 if (next_ext <= this_ext) 226 break; 227 this_ext = next_ext; 228 } 229 #ifdef COMPAT_386BSD_MBRPART 230 if (this_ext == 0 && dp_386bsd != -1) 231 return (*actn)(a, &ptns[dp_386bsd], ap_386bsd, 0); 232 #endif 233 return SCAN_CONTINUE; 234 } 235 236 237 static void 238 scan_iso_vrs_session(mbr_args_t *a, uint32_t first_sector, 239 int *is_iso9660, int *is_udf) 240 { 241 struct vrs_desc *vrsd; 242 uint64_t vrs; 243 int sector_size; 244 int blks, inc; 245 246 sector_size = a->lp->d_secsize; 247 blks = sector_size / DEV_BSIZE; 248 inc = MAX(1, 2048 / sector_size); 249 250 /* by definition */ 251 vrs = ((32*1024 + sector_size - 1) / sector_size) 252 + first_sector; 253 254 /* read first vrs sector */ 255 if (read_sector(a, vrs * blks, 1)) 256 return; 257 258 /* skip all CD001 records */ 259 vrsd = a->bp->b_data; 260 /* printf("vrsd->identifier = `%s`\n", vrsd->identifier); */ 261 while (memcmp(vrsd->identifier, "CD001", 5) == 0) { 262 /* for sure */ 263 *is_iso9660 = first_sector; 264 265 vrs += inc; 266 if (read_sector(a, vrs * blks, 1)) 267 return; 268 } 269 270 /* search for BEA01 */ 271 vrsd = a->bp->b_data; 272 /* printf("vrsd->identifier = `%s`\n", vrsd->identifier); */ 273 if (memcmp(vrsd->identifier, "BEA01", 5)) 274 return; 275 276 /* read successor */ 277 vrs += inc; 278 if (read_sector(a, vrs * blks, 1)) 279 return; 280 281 /* check for NSR[23] */ 282 vrsd = a->bp->b_data; 283 /* printf("vrsd->identifier = `%s`\n", vrsd->identifier); */ 284 if (memcmp(vrsd->identifier, "NSR0", 4)) 285 return; 286 287 *is_udf = first_sector; 288 } 289 290 291 /* 292 * Scan for ISO Volume Recognition Sequences 293 */ 294 295 static int 296 scan_iso_vrs(mbr_args_t *a) 297 { 298 struct mmc_discinfo di; 299 struct mmc_trackinfo ti; 300 dev_t dev; 301 uint64_t sector; 302 int is_iso9660, is_udf; 303 int tracknr, sessionnr; 304 int new_session, error; 305 306 is_iso9660 = is_udf = -1; 307 308 /* parse all sessions of disc if we're on a SCSI MMC device */ 309 if (a->lp->d_flags & D_SCSI_MMC) { 310 /* get disc info */ 311 dev = a->bp->b_dev; 312 error = bdev_ioctl(dev, MMCGETDISCINFO, &di, FKIOCTL, curlwp); 313 if (error) 314 return SCAN_CONTINUE; 315 316 /* go trough all (data) tracks */ 317 sessionnr = -1; 318 for (tracknr = di.first_track; 319 tracknr <= di.first_track_last_session; tracknr++) 320 { 321 ti.tracknr = tracknr; 322 error = bdev_ioctl(dev, MMCGETTRACKINFO, &ti, 323 FKIOCTL, curlwp); 324 if (error) 325 return SCAN_CONTINUE; 326 new_session = (ti.sessionnr != sessionnr); 327 sessionnr = ti.sessionnr; 328 if (new_session) { 329 if (ti.flags & MMC_TRACKINFO_BLANK) 330 continue; 331 if (!(ti.flags & MMC_TRACKINFO_DATA)) 332 continue; 333 sector = ti.track_start; 334 scan_iso_vrs_session(a, sector, 335 &is_iso9660, &is_udf); 336 } 337 } 338 if (is_udf < 0) { 339 /* defaulting udf on the RAW partition */ 340 is_udf = 0; 341 } 342 } else { 343 /* try start of disc */ 344 sector = 0; 345 scan_iso_vrs_session(a, sector, &is_iso9660, &is_udf); 346 } 347 348 if ((is_iso9660 < 0) && (is_udf < 0)) 349 return SCAN_CONTINUE; 350 351 strncpy(a->lp->d_typename, "iso partition", 16); 352 353 /* add iso9660 partition if found */ 354 if (is_iso9660 >= 0) { 355 /* set 'a' partition to iso9660 */ 356 a->lp->d_partitions[0].p_offset = 0; 357 a->lp->d_partitions[0].p_size = a->lp->d_secperunit; 358 a->lp->d_partitions[0].p_cdsession = is_iso9660; 359 a->lp->d_partitions[0].p_fstype = FS_ISO9660; 360 } else { 361 a->lp->d_partitions[0].p_size = 0; 362 a->lp->d_partitions[0].p_fstype = FS_UNUSED; 363 } 364 365 /* add udf partition if found */ 366 if (is_udf >= 0) { 367 /* set the RAW partion to UDF for CD/USB stick etc */ 368 a->lp->d_partitions[RAW_PART].p_fstype = FS_UDF; 369 /* UDF doesn't care about the cd session specified here */ 370 } 371 372 return SCAN_FOUND; 373 } 374 375 376 /* 377 * Attempt to read a disk label from a device 378 * using the indicated strategy routine. 379 * The label must be partly set up before this: 380 * secpercyl, secsize and anything required for a block i/o read 381 * operation in the driver's strategy/start routines 382 * must be filled in before calling us. 383 * 384 * If dos partition table requested, attempt to load it and 385 * find disklabel inside a DOS partition. Also, if bad block 386 * table needed, attempt to extract it as well. Return buffer 387 * for use in signalling errors if requested. 388 * 389 * Returns null on success and an error string on failure. 390 */ 391 const char * 392 readdisklabel(dev_t dev, void (*strat)(struct buf *), struct disklabel *lp, 393 struct cpu_disklabel *osdep) 394 { 395 struct dkbad *bdp; 396 int rval; 397 int i; 398 mbr_args_t a; 399 400 memset(&a, 0, sizeof a); 401 a.lp = lp; 402 a.strat = strat; 403 a.action = READ_LABEL; 404 405 /* minimal requirements for architypal disk label */ 406 if (lp->d_secsize == 0) 407 lp->d_secsize = DEV_BSIZE; 408 if (lp->d_secperunit == 0) 409 lp->d_secperunit = 0x1fffffff; 410 a.secperunit = lp->d_secperunit; 411 lp->d_npartitions = RAW_PART + 1; 412 for (i = 0; i < RAW_PART; i++) { 413 lp->d_partitions[i].p_size = 0; 414 lp->d_partitions[i].p_offset = 0; 415 } 416 if (lp->d_partitions[RAW_PART].p_size == 0) 417 lp->d_partitions[RAW_PART].p_size = lp->d_secperunit; 418 lp->d_partitions[RAW_PART].p_offset = 0; 419 420 /* 421 * Set partition 'a' to be the whole disk. 422 * Cleared if we find an mbr or a netbsd label. 423 */ 424 lp->d_partitions[0].p_size = lp->d_partitions[RAW_PART].p_size; 425 lp->d_partitions[0].p_fstype = FS_BSDFFS; 426 427 /* 428 * Get a buffer big enough to read a disklabel in and initialize it 429 * make it two sectors long for the validate_label(); see comment at 430 * start of file. 431 */ 432 a.bp = geteblk(2 * (int)lp->d_secsize); 433 a.bp->b_dev = dev; 434 435 if (osdep) 436 /* 437 * Scan mbr searching for netbsd partition and saving 438 * bios partition information to use if the netbsd one 439 * is absent. 440 */ 441 rval = scan_mbr(&a, look_netbsd_part); 442 else 443 rval = SCAN_CONTINUE; 444 445 if (rval == SCAN_CONTINUE) { 446 /* Look at start of disk */ 447 rval = validate_label(&a, 0); 448 } 449 450 if (rval == SCAN_CONTINUE) { 451 rval = scan_iso_vrs(&a); 452 } 453 #if 0 454 /* 455 * Save sector where we found the label for the 'don't overwrite 456 * the label' check in bounds_check_with_label. 457 */ 458 if (rval == SCAN_FOUND) 459 xxx->label_sector = a.label_sector; 460 #endif 461 462 /* Obtain bad sector table if requested and present */ 463 if (rval == SCAN_FOUND && osdep && (lp->d_flags & D_BADSECT)) { 464 struct dkbad *db; 465 int blkno; 466 467 bdp = &osdep->bad; 468 i = 0; 469 rval = SCAN_ERROR; 470 do { 471 /* read a bad sector table */ 472 blkno = lp->d_secperunit - lp->d_nsectors + i; 473 if (lp->d_secsize > DEV_BSIZE) 474 blkno *= lp->d_secsize / DEV_BSIZE; 475 else 476 blkno /= DEV_BSIZE / lp->d_secsize; 477 /* if successful, validate, otherwise try another */ 478 if (read_sector(&a, blkno, 1)) { 479 a.msg = "bad sector table I/O error"; 480 continue; 481 } 482 db = (struct dkbad *)(a.bp->b_data); 483 #define DKBAD_MAGIC 0x4321 484 if (db->bt_mbz != 0 || db->bt_flag != DKBAD_MAGIC) { 485 a.msg = "bad sector table corrupted"; 486 continue; 487 } 488 rval = SCAN_FOUND; 489 *bdp = *db; 490 break; 491 } while (a.bp->b_error && (i += 2) < 10 && 492 i < lp->d_nsectors); 493 } 494 495 brelse(a.bp, 0); 496 if (rval == SCAN_ERROR || rval == SCAN_CONTINUE) 497 return a.msg; 498 return NULL; 499 } 500 501 static int 502 look_netbsd_part(mbr_args_t *a, mbr_partition_t *dp, int slot, uint ext_base) 503 { 504 struct partition *pp; 505 int ptn_base = ext_base + le32toh(dp->mbrp_start); 506 int rval; 507 508 if ( 509 #ifdef COMPAT_386BSD_MBRPART 510 dp->mbrp_type == MBR_PTYPE_386BSD || 511 #endif 512 dp->mbrp_type == MBR_PTYPE_NETBSD) { 513 rval = validate_label(a, ptn_base); 514 515 #if RAW_PART == 3 516 /* Put actual location where we found the label into ptn 2 */ 517 if (rval == SCAN_FOUND || a->lp->d_partitions[2].p_size == 0) { 518 a->lp->d_partitions[2].p_size = le32toh(dp->mbrp_size); 519 a->lp->d_partitions[2].p_offset = ptn_base; 520 } 521 #endif 522 523 /* If we got a netbsd label look no further */ 524 if (rval == SCAN_FOUND) 525 return rval; 526 } 527 528 /* Install main partitions into e..h and extended into i+ */ 529 if (ext_base == 0) 530 slot += 4; 531 else { 532 slot = 4 + MBR_PART_COUNT; 533 pp = &a->lp->d_partitions[slot]; 534 for (; slot < MAXPARTITIONS; pp++, slot++) { 535 /* This gets called twice - avoid duplicates */ 536 if (pp->p_offset == ptn_base && 537 pp->p_size == le32toh(dp->mbrp_size)) 538 break; 539 if (pp->p_size == 0) 540 break; 541 } 542 } 543 544 if (slot < MAXPARTITIONS) { 545 /* Stop 'a' being the entire disk */ 546 a->lp->d_partitions[0].p_size = 0; 547 a->lp->d_partitions[0].p_fstype = 0; 548 549 /* save partition info */ 550 pp = &a->lp->d_partitions[slot]; 551 pp->p_offset = ptn_base; 552 pp->p_size = le32toh(dp->mbrp_size); 553 pp->p_fstype = xlat_mbr_fstype(dp->mbrp_type); 554 555 if (slot >= a->lp->d_npartitions) 556 a->lp->d_npartitions = slot + 1; 557 } 558 559 return SCAN_CONTINUE; 560 } 561 562 563 static int 564 validate_label(mbr_args_t *a, uint label_sector) 565 { 566 struct disklabel *dlp; 567 char *dlp_lim, *dlp_byte; 568 int error; 569 570 /* Next, dig out disk label */ 571 if (read_sector(a, label_sector, 2)) { 572 a->msg = "disk label read failed"; 573 return SCAN_ERROR; 574 } 575 576 /* Locate disk label within block and validate */ 577 /* 578 * XXX (dsl) This search may be a waste of time, a lot of other i386 579 * code assumes the label is at offset LABELOFFSET (=0) in the sector. 580 * 581 * If we want to support disks from other netbsd ports, then the 582 * code should also allow for a shorter label nearer the end of 583 * the disk sector, and (IIRC) labels within 8k of the disk start. 584 */ 585 dlp = (void *)a->bp->b_data; 586 dlp_lim = (char *)a->bp->b_data + a->bp->b_bcount - sizeof *dlp; 587 for (;; dlp = (void *)((char *)dlp + sizeof(long))) { 588 if ((char *)dlp > dlp_lim) { 589 if (a->action != WRITE_LABEL) 590 return SCAN_CONTINUE; 591 /* Write at arch. dependant default location */ 592 dlp_byte = (char *)a->bp->b_data + LABELOFFSET; 593 if (label_sector) 594 dlp_byte += MBR_LABELSECTOR * a->lp->d_secsize; 595 else 596 dlp_byte += LABELSECTOR * a->lp->d_secsize; 597 dlp = (void *)dlp_byte; 598 break; 599 } 600 if (dlp->d_magic != DISKMAGIC || dlp->d_magic2 != DISKMAGIC) 601 continue; 602 if (dlp->d_npartitions > MAXPARTITIONS || dkcksum(dlp) != 0) { 603 a->msg = "disk label corrupted"; 604 continue; 605 } 606 break; 607 } 608 609 switch (a->action) { 610 case READ_LABEL: 611 *a->lp = *dlp; 612 if ((a->msg = convertdisklabel(a->lp, a->strat, a->bp, 613 a->secperunit)) != NULL) 614 return SCAN_ERROR; 615 a->label_sector = label_sector; 616 return SCAN_FOUND; 617 case UPDATE_LABEL: 618 case WRITE_LABEL: 619 *dlp = *a->lp; 620 a->bp->b_oflags &= ~BO_DONE; 621 a->bp->b_flags &= ~B_READ; 622 a->bp->b_flags |= B_WRITE; 623 (*a->strat)(a->bp); 624 error = biowait(a->bp); 625 if (error != 0) { 626 a->error = error; 627 a->msg = "disk label write failed"; 628 return SCAN_ERROR; 629 } 630 a->written++; 631 /* Write label to all mbr partitions */ 632 return SCAN_CONTINUE; 633 default: 634 return SCAN_ERROR; 635 } 636 } 637 638 /* 639 * Check new disk label for sensibility 640 * before setting it. 641 */ 642 int 643 setdisklabel(struct disklabel *olp, struct disklabel *nlp, u_long openmask, 644 struct cpu_disklabel *osdep) 645 { 646 int i; 647 struct partition *opp, *npp; 648 649 /* sanity clause */ 650 if (nlp->d_secpercyl == 0 || nlp->d_secsize == 0 651 || (nlp->d_secsize % DEV_BSIZE) != 0) 652 return (EINVAL); 653 654 /* special case to allow disklabel to be invalidated */ 655 if (nlp->d_magic == 0xffffffff) { 656 *olp = *nlp; 657 return (0); 658 } 659 660 if (nlp->d_magic != DISKMAGIC || nlp->d_magic2 != DISKMAGIC || 661 dkcksum(nlp) != 0) 662 return (EINVAL); 663 664 /* XXX missing check if other dos partitions will be overwritten */ 665 666 while (openmask != 0) { 667 i = ffs(openmask) - 1; 668 openmask &= ~(1 << i); 669 if (i > nlp->d_npartitions) 670 return (EBUSY); 671 opp = &olp->d_partitions[i]; 672 npp = &nlp->d_partitions[i]; 673 /* 674 * Copy internally-set partition information 675 * if new label doesn't include it. XXX 676 */ 677 if (npp->p_fstype == FS_UNUSED && opp->p_fstype != FS_UNUSED) { 678 *npp = *opp; 679 continue; 680 } 681 if (npp->p_offset != opp->p_offset || npp->p_size < opp->p_size) 682 return (EBUSY); 683 } 684 nlp->d_checksum = 0; 685 nlp->d_checksum = dkcksum(nlp); 686 *olp = *nlp; 687 return (0); 688 } 689 690 691 /* 692 * Write disk label back to device after modification. 693 */ 694 int 695 writedisklabel(dev_t dev, void (*strat)(struct buf *), struct disklabel *lp, 696 struct cpu_disklabel *osdep) 697 { 698 mbr_args_t a; 699 700 memset(&a, 0, sizeof a); 701 a.lp = lp; 702 a.strat = strat; 703 704 /* get a buffer and initialize it */ 705 a.bp = geteblk(2 * (int)lp->d_secsize); 706 a.bp->b_dev = dev; 707 708 /* osdep => we expect an mbr with label in netbsd ptn */ 709 a.action = osdep != NULL ? WRITE_LABEL : UPDATE_LABEL; 710 711 /* Write/update the label to every netbsd mbr partition */ 712 scan_mbr(&a, write_netbsd_label); 713 714 /* Old write the label at the start of the volume on disks that 715 * don't have a valid mbr (always update an existing one) */ 716 a.action = a.found_mbr ? UPDATE_LABEL : WRITE_LABEL; 717 validate_label(&a, 0); 718 719 if (a.written == 0 && a.error == 0) 720 a.error = ESRCH; 721 722 brelse(a.bp, 0); 723 return a.error; 724 } 725 726 static int 727 write_netbsd_label(mbr_args_t *a, mbr_partition_t *dp, int slot, uint ext_base) 728 { 729 int ptn_base = ext_base + le32toh(dp->mbrp_start); 730 731 if (dp->mbrp_type != MBR_PTYPE_NETBSD) 732 return SCAN_CONTINUE; 733 734 return validate_label(a, ptn_base); 735 } 736