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