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