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