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