1 /* $NetBSD: subr_disk_mbr.c,v 1.52 2019/11/06 13:07:32 kamil 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.52 2019/11/06 13:07:32 kamil 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 static bool 572 check_label_magic(const struct disklabel *dlp, uint32_t diskmagic) 573 { 574 575 return memcmp((const char *)dlp + offsetof(struct disklabel, d_magic), 576 &diskmagic, sizeof(diskmagic)) == 0 && 577 memcmp((const char *)dlp + offsetof(struct disklabel, d_magic2), 578 &diskmagic, sizeof(diskmagic)) == 0; 579 } 580 581 #ifdef DISKLABEL_EI 582 /* 583 * - For read, convert a label to the native byte order. 584 * - For update or write, if a label already exists, keep its byte order. 585 * Otherwise, write a new label in the native byte order. 586 */ 587 #endif 588 static int 589 validate_label(mbr_args_t *a, uint label_sector) 590 { 591 struct disklabel *dlp; 592 char *dlp_lim, *dlp_byte; 593 int error; 594 #ifdef DISKLABEL_EI 595 int swapped = 0; 596 uint16_t npartitions; 597 #endif 598 599 /* Next, dig out disk label */ 600 if (read_sector(a, label_sector, SCANBLOCKS)) { 601 a->msg = "disk label read failed"; 602 return SCAN_ERROR; 603 } 604 605 /* Locate disk label within block and validate */ 606 /* 607 * XXX (dsl) This search may be a waste of time, a lot of other i386 608 * code assumes the label is at offset LABELOFFSET (=0) in the sector. 609 * 610 * If we want to support disks from other netbsd ports, then the 611 * code should also allow for a shorter label nearer the end of 612 * the disk sector, and (IIRC) labels within 8k of the disk start. 613 */ 614 dlp = (void *)a->bp->b_data; 615 dlp_lim = (char *)a->bp->b_data + a->bp->b_bcount - sizeof *dlp; 616 for (;; dlp = (void *)((char *)dlp + sizeof(uint32_t))) { 617 if ((char *)dlp > dlp_lim) { 618 if (a->action != WRITE_LABEL) 619 return SCAN_CONTINUE; 620 /* Write at arch. dependent default location */ 621 dlp_byte = (char *)a->bp->b_data + LABELOFFSET; 622 if (label_sector) 623 dlp_byte += MBR_LABELSECTOR * a->lp->d_secsize; 624 else 625 dlp_byte += LABELSECTOR * a->lp->d_secsize; 626 dlp = (void *)dlp_byte; 627 break; 628 } 629 if (!check_label_magic(dlp, DISKMAGIC)) 630 #ifdef DISKLABEL_EI 631 { 632 if (!check_label_magic(dlp, bswap32(DISKMAGIC))) 633 continue; 634 635 /* 636 * The label is in the other byte order. We need to 637 * checksum before swapping the byte order. 638 */ 639 npartitions = bswap16(dlp->d_npartitions); 640 if (npartitions > MAXPARTITIONS || 641 dkcksum_sized(dlp, npartitions) != 0) 642 goto corrupted; 643 644 swapped = 1; 645 } 646 #else 647 continue; 648 #endif 649 else if (dlp->d_npartitions > MAXPARTITIONS || 650 dkcksum(dlp) != 0) { 651 #ifdef DISKLABEL_EI 652 corrupted: 653 #endif 654 a->msg = "disk label corrupted"; 655 continue; 656 } 657 break; 658 } 659 660 switch (a->action) { 661 case READ_LABEL: 662 #ifdef DISKLABEL_EI 663 if (swapped) 664 swap_disklabel(a->lp, dlp); 665 else 666 *a->lp = *dlp; 667 #else 668 *a->lp = *dlp; 669 #endif 670 if ((a->msg = convertdisklabel(a->lp, a->strat, a->bp, 671 a->secperunit)) != NULL) 672 return SCAN_ERROR; 673 a->label_sector = label_sector; 674 return SCAN_FOUND; 675 case UPDATE_LABEL: 676 case WRITE_LABEL: 677 #ifdef DISKLABEL_EI 678 /* DO NOT swap a->lp itself for later references. */ 679 if (swapped) 680 swap_disklabel(dlp, a->lp); 681 else 682 *dlp = *a->lp; 683 #else 684 *dlp = *a->lp; 685 #endif 686 a->bp->b_oflags &= ~BO_DONE; 687 a->bp->b_flags &= ~B_READ; 688 a->bp->b_flags |= B_WRITE; 689 (*a->strat)(a->bp); 690 error = biowait(a->bp); 691 if (error != 0) { 692 a->error = error; 693 a->msg = "disk label write failed"; 694 return SCAN_ERROR; 695 } 696 a->written++; 697 /* Write label to all mbr partitions */ 698 return SCAN_CONTINUE; 699 default: 700 return SCAN_ERROR; 701 } 702 } 703 704 /* 705 * Write disk label back to device after modification. 706 */ 707 int 708 writedisklabel(dev_t dev, void (*strat)(struct buf *), struct disklabel *lp, 709 struct cpu_disklabel *osdep) 710 { 711 mbr_args_t a; 712 713 memset(&a, 0, sizeof a); 714 a.lp = lp; 715 a.strat = strat; 716 717 /* get a buffer and initialize it */ 718 a.bp = geteblk(SCANBLOCKS * (int)lp->d_secsize); 719 a.bp->b_dev = dev; 720 721 /* osdep => we expect an mbr with label in netbsd ptn */ 722 a.action = osdep != NULL ? WRITE_LABEL : UPDATE_LABEL; 723 724 /* Write/update the label to every netbsd mbr partition */ 725 scan_mbr(&a, write_netbsd_label); 726 727 /* Old write the label at the start of the volume on disks that 728 * don't have a valid mbr (always update an existing one) */ 729 a.action = a.found_mbr ? UPDATE_LABEL : WRITE_LABEL; 730 validate_label(&a, 0); 731 732 if (a.written == 0 && a.error == 0) 733 a.error = ESRCH; 734 735 brelse(a.bp, 0); 736 return a.error; 737 } 738 739 static int 740 write_netbsd_label(mbr_args_t *a, mbr_partition_t *dp, int slot, uint ext_base) 741 { 742 int ptn_base = ext_base + le32toh(dp->mbrp_start); 743 744 if (dp->mbrp_type != MBR_PTYPE_NETBSD) 745 return SCAN_CONTINUE; 746 747 return validate_label(a, ptn_base); 748 } 749 750 #ifdef DISKLABEL_EI 751 /* 752 * from sh3/disksubr.c with modifications: 753 * - update d_checksum properly 754 * - replace memcpy(9) by memmove(9) as a precaution 755 */ 756 static void 757 swap_disklabel(struct disklabel *nlp, struct disklabel *olp) 758 { 759 int i; 760 uint16_t npartitions; 761 762 #define SWAP16(x) nlp->x = bswap16(olp->x) 763 #define SWAP32(x) nlp->x = bswap32(olp->x) 764 765 SWAP32(d_magic); 766 SWAP16(d_type); 767 SWAP16(d_subtype); 768 /* Do not need to swap char strings. */ 769 memmove(nlp->d_typename, olp->d_typename, sizeof(nlp->d_typename)); 770 771 /* XXX What should we do for d_un (an union of char and pointers) ? */ 772 memmove(nlp->d_packname, olp->d_packname, sizeof(nlp->d_packname)); 773 774 SWAP32(d_secsize); 775 SWAP32(d_nsectors); 776 SWAP32(d_ntracks); 777 SWAP32(d_ncylinders); 778 SWAP32(d_secpercyl); 779 SWAP32(d_secperunit); 780 781 SWAP16(d_sparespertrack); 782 SWAP16(d_sparespercyl); 783 784 SWAP32(d_acylinders); 785 786 SWAP16(d_rpm); 787 SWAP16(d_interleave); 788 SWAP16(d_trackskew); 789 SWAP16(d_cylskew); 790 SWAP32(d_headswitch); 791 SWAP32(d_trkseek); 792 SWAP32(d_flags); 793 for (i = 0; i < NDDATA; i++) 794 SWAP32(d_drivedata[i]); 795 for (i = 0; i < NSPARE; i++) 796 SWAP32(d_spare[i]); 797 SWAP32(d_magic2); 798 /* d_checksum is updated later. */ 799 800 SWAP16(d_npartitions); 801 SWAP32(d_bbsize); 802 SWAP32(d_sbsize); 803 for (i = 0; i < MAXPARTITIONS; i++) { 804 SWAP32(d_partitions[i].p_size); 805 SWAP32(d_partitions[i].p_offset); 806 SWAP32(d_partitions[i].p_fsize); 807 /* p_fstype and p_frag is uint8_t, so no need to swap. */ 808 nlp->d_partitions[i].p_fstype = olp->d_partitions[i].p_fstype; 809 nlp->d_partitions[i].p_frag = olp->d_partitions[i].p_frag; 810 SWAP16(d_partitions[i].p_cpg); 811 } 812 813 #undef SWAP16 814 #undef SWAP32 815 816 /* Update checksum in the target endian. */ 817 nlp->d_checksum = 0; 818 npartitions = nlp->d_magic == DISKMAGIC ? 819 nlp->d_npartitions : olp->d_npartitions; 820 /* 821 * npartitions can be larger than MAXPARTITIONS when the label was not 822 * validated by setdisklabel. If so, the label is intentionally(?) 823 * corrupted and checksum should be meaningless. 824 */ 825 if (npartitions <= MAXPARTITIONS) 826 nlp->d_checksum = dkcksum_sized(nlp, npartitions); 827 } 828 #endif /* DISKLABEL_EI */ 829