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