1 /* $NetBSD: subr_disk_mbr.c,v 1.16 2005/12/27 15:45:09 jmmv 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 partition a 42 * is set to cover the whole disk. 43 * Useful for 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.16 2005/12/27 15:45:09 jmmv Exp $"); 58 59 #include <sys/param.h> 60 #include <sys/systm.h> 61 #include <sys/buf.h> 62 #include <sys/disklabel.h> 63 #include <sys/disk.h> 64 #include <sys/syslog.h> 65 66 #include "opt_mbr.h" 67 68 typedef struct mbr_partition mbr_partition_t; 69 70 #define MBR_LABELSECTOR 1 71 72 #define SCAN_CONTINUE 0 73 #define SCAN_FOUND 1 74 #define SCAN_ERROR 2 75 76 typedef struct mbr_args { 77 struct disklabel *lp; 78 void (*strat)(struct buf *); 79 struct buf *bp; 80 const char *msg; 81 int error; 82 int written; /* number of times we wrote label */ 83 uint label_sector; /* where we found the label */ 84 } mbr_args_t; 85 86 #define READ_LABEL 1 87 #define UPDATE_LABEL 2 88 #define WRITE_LABEL 3 89 static int validate_label(mbr_args_t *, uint, int); 90 static int look_netbsd_part(mbr_args_t *, mbr_partition_t *, int, uint); 91 static int write_netbsd_label(mbr_args_t *, mbr_partition_t *, int, uint); 92 93 static int 94 read_sector(mbr_args_t *a, uint sector) 95 { 96 struct buf *bp = a->bp; 97 int error; 98 99 bp->b_blkno = sector; 100 bp->b_bcount = a->lp->d_secsize; 101 bp->b_flags = (bp->b_flags & ~(B_WRITE | B_DONE)) | B_READ; 102 bp->b_cylinder = sector / a->lp->d_secpercyl; 103 (*a->strat)(bp); 104 error = biowait(bp); 105 if (error != 0) 106 a->error = error; 107 return error; 108 } 109 110 /* 111 * Scan MBR for partitions, call 'action' routine for each. 112 */ 113 114 static int 115 scan_mbr(mbr_args_t *a, int (*actn)(mbr_args_t *, mbr_partition_t *, int, uint)) 116 { 117 mbr_partition_t ptns[MBR_PART_COUNT]; 118 mbr_partition_t *dp; 119 struct mbr_sector *mbr; 120 uint ext_base, this_ext, next_ext; 121 int rval; 122 int i; 123 #ifdef COMPAT_386BSD_MBRPART 124 int dp_386bsd = -1; 125 #endif 126 127 ext_base = 0; 128 this_ext = 0; 129 for (;;) { 130 if (read_sector(a, this_ext)) { 131 a->msg = "dos partition I/O error"; 132 return SCAN_ERROR; 133 } 134 135 /* Note: Magic number is little-endian. */ 136 mbr = (void *)a->bp->b_data; 137 if (mbr->mbr_magic != htole16(MBR_MAGIC)) 138 return SCAN_CONTINUE; 139 140 /* Copy data out of buffer so action can use bp */ 141 memcpy(ptns, &mbr->mbr_parts, sizeof ptns); 142 143 /* Look for drivers and skip them */ 144 if (ptns[0].mbrp_type == MBR_PTYPE_DM6_DDO) { 145 /* We've found a DM6 DDO partition type (used by 146 * the Ontrack Disk Manager drivers). 147 * 148 * Ensure that there are no other partitions in the 149 * MBR and jump to the real partition table (stored 150 * in the first sector of the second track). */ 151 boolean_t ok = TRUE; 152 153 for (i = 1; i < MBR_PART_COUNT; i++) 154 if (ptns[i].mbrp_type != MBR_PTYPE_UNUSED) 155 ok = FALSE; 156 157 if (ok) { 158 this_ext = le32toh(a->lp->d_secpercyl / 159 a->lp->d_ntracks); 160 continue; 161 } 162 } 163 164 /* look for NetBSD partition */ 165 next_ext = 0; 166 dp = ptns; 167 for (i = 0; i < MBR_PART_COUNT; i++, dp++) { 168 if (dp->mbrp_type == 0) 169 continue; 170 if (MBR_IS_EXTENDED(dp->mbrp_type)) { 171 next_ext = le32toh(dp->mbrp_start); 172 continue; 173 } 174 #ifdef COMPAT_386BSD_MBRPART 175 if (dp->mbrp_type == MBR_PTYPE_386BSD) { 176 /* 177 * If more than one matches, take last, 178 * as NetBSD install tool does. 179 */ 180 if (this_ext == 0) 181 dp_386bsd = i; 182 continue; 183 } 184 #endif 185 rval = (*actn)(a, dp, i, this_ext); 186 if (rval != SCAN_CONTINUE) 187 return rval; 188 } 189 if (next_ext == 0) 190 break; 191 if (ext_base == 0) { 192 ext_base = next_ext; 193 next_ext = 0; 194 } 195 next_ext += ext_base; 196 if (next_ext <= this_ext) 197 break; 198 this_ext = next_ext; 199 } 200 #ifdef COMPAT_386BSD_MBRPART 201 if (this_ext == 0 && dp_386bsd != -1) 202 return (*actn)(a, &ptns[dp_386bsd], dp_386bsd, 0); 203 #endif 204 return SCAN_CONTINUE; 205 } 206 207 /* 208 * Attempt to read a disk label from a device 209 * using the indicated strategy routine. 210 * The label must be partly set up before this: 211 * secpercyl, secsize and anything required for a block i/o read 212 * operation in the driver's strategy/start routines 213 * must be filled in before calling us. 214 * 215 * If dos partition table requested, attempt to load it and 216 * find disklabel inside a DOS partition. Also, if bad block 217 * table needed, attempt to extract it as well. Return buffer 218 * for use in signalling errors if requested. 219 * 220 * Returns null on success and an error string on failure. 221 */ 222 const char * 223 readdisklabel(dev_t dev, void (*strat)(struct buf *), struct disklabel *lp, 224 struct cpu_disklabel *osdep) 225 { 226 struct dkbad *bdp; 227 int rval; 228 int i; 229 mbr_args_t a; 230 231 memset(&a, 0, sizeof a); 232 a.lp = lp; 233 a.strat = strat; 234 235 /* minimal requirements for architypal disk label */ 236 if (lp->d_secsize == 0) 237 lp->d_secsize = DEV_BSIZE; 238 if (lp->d_secperunit == 0) 239 lp->d_secperunit = 0x1fffffff; 240 lp->d_npartitions = RAW_PART + 1; 241 for (i = 0; i < RAW_PART; i++) { 242 lp->d_partitions[i].p_size = 0; 243 lp->d_partitions[i].p_offset = 0; 244 } 245 if (lp->d_partitions[RAW_PART].p_size == 0) 246 lp->d_partitions[RAW_PART].p_size = 0x1fffffff; 247 lp->d_partitions[RAW_PART].p_offset = 0; 248 249 /* 250 * Set partition 'a' to be the whole disk. 251 * Cleared if we find an mbr or a netbsd label. 252 */ 253 lp->d_partitions[0].p_size = lp->d_partitions[RAW_PART].p_size; 254 lp->d_partitions[0].p_fstype = FS_BSDFFS; 255 256 /* get a buffer and initialize it */ 257 a.bp = geteblk((int)lp->d_secsize); 258 a.bp->b_dev = dev; 259 260 if (osdep) 261 /* 262 * Scan mbr searching for netbsd partition and saving 263 * bios partition information to use if the netbsd one 264 * is absent. 265 */ 266 rval = scan_mbr(&a, look_netbsd_part); 267 else 268 rval = SCAN_CONTINUE; 269 270 if (rval == SCAN_CONTINUE) { 271 /* Look at start of disk */ 272 rval = validate_label(&a, LABELSECTOR, READ_LABEL); 273 if (LABELSECTOR != 0 && rval == SCAN_CONTINUE) 274 rval = validate_label(&a, 0, READ_LABEL); 275 } 276 277 #if 0 278 /* 279 * Save sector where we found the label for the 'don't overwrite 280 * the label' check in bounds_check_with_label. 281 */ 282 if (rval == SCAN_FOUND) 283 xxx->label_sector = a.label_sector; 284 #endif 285 286 /* Obtain bad sector table if requested and present */ 287 if (rval == SCAN_FOUND && osdep && (lp->d_flags & D_BADSECT)) { 288 struct dkbad *db; 289 int blkno; 290 291 bdp = &osdep->bad; 292 i = 0; 293 rval = SCAN_ERROR; 294 do { 295 /* read a bad sector table */ 296 blkno = lp->d_secperunit - lp->d_nsectors + i; 297 if (lp->d_secsize > DEV_BSIZE) 298 blkno *= lp->d_secsize / DEV_BSIZE; 299 else 300 blkno /= DEV_BSIZE / lp->d_secsize; 301 /* if successful, validate, otherwise try another */ 302 if (read_sector(&a, blkno)) { 303 a.msg = "bad sector table I/O error"; 304 continue; 305 } 306 db = (struct dkbad *)(a.bp->b_data); 307 #define DKBAD_MAGIC 0x4321 308 if (db->bt_mbz != 0 || db->bt_flag != DKBAD_MAGIC) { 309 a.msg = "bad sector table corrupted"; 310 continue; 311 } 312 rval = SCAN_FOUND; 313 *bdp = *db; 314 break; 315 } while ((a.bp->b_flags & B_ERROR) && (i += 2) < 10 && 316 i < lp->d_nsectors); 317 } 318 319 brelse(a.bp); 320 if (rval == SCAN_ERROR) 321 return a.msg; 322 return NULL; 323 } 324 325 static int 326 look_netbsd_part(mbr_args_t *a, mbr_partition_t *dp, int slot, uint ext_base) 327 { 328 struct partition *pp; 329 int ptn_base = ext_base + le32toh(dp->mbrp_start); 330 int rval; 331 332 if ( 333 #ifdef COMPAT_386BSD_MBRPART 334 dp->mbrp_type == MBR_PTYPE_386BSD || 335 #endif 336 dp->mbrp_type == MBR_PTYPE_NETBSD) { 337 rval = validate_label(a, ptn_base + MBR_LABELSECTOR, READ_LABEL); 338 339 #if RAW_PART == 3 340 /* Put actual location where we found the label into ptn 2 */ 341 if (rval == SCAN_FOUND || a->lp->d_partitions[2].p_size == 0) { 342 a->lp->d_partitions[2].p_size = le32toh(dp->mbrp_size); 343 a->lp->d_partitions[2].p_offset = ptn_base; 344 } 345 #endif 346 347 /* If we got a netbsd label look no further */ 348 if (rval == SCAN_FOUND) 349 return rval; 350 } 351 352 /* Install main partitions into e..h and extended into i+ */ 353 if (ext_base == 0) 354 slot += 4; 355 else { 356 slot = 4 + MBR_PART_COUNT; 357 pp = &a->lp->d_partitions[slot]; 358 for (; slot < MAXPARTITIONS; pp++, slot++) { 359 /* This gets called twice - avoid duplicates */ 360 if (pp->p_offset == ptn_base && 361 pp->p_size == le32toh(dp->mbrp_size)) 362 break; 363 if (pp->p_size == 0) 364 break; 365 } 366 } 367 368 if (slot < MAXPARTITIONS) { 369 /* Stop 'a' being the entire disk */ 370 a->lp->d_partitions[0].p_size = 0; 371 a->lp->d_partitions[0].p_fstype = 0; 372 373 /* save partition info */ 374 pp = &a->lp->d_partitions[slot]; 375 pp->p_offset = ptn_base; 376 pp->p_size = le32toh(dp->mbrp_size); 377 pp->p_fstype = xlat_mbr_fstype(dp->mbrp_type); 378 379 if (slot >= a->lp->d_npartitions) 380 a->lp->d_npartitions = slot + 1; 381 } 382 383 return SCAN_CONTINUE; 384 } 385 386 387 static int 388 validate_label(mbr_args_t *a, uint label_sector, int action) 389 { 390 struct disklabel *dlp; 391 char *dlp_lim; 392 int error; 393 394 /* Next, dig out disk label */ 395 if (read_sector(a, label_sector)) { 396 a->msg = "disk label read failed"; 397 return SCAN_ERROR; 398 } 399 400 /* Locate disk label within block and validate */ 401 /* 402 * XXX (dsl) This search may be a waste of time, a lot of other i386 403 * code assumes the label is at offset LABELOFFSET (=0) in the sector. 404 * 405 * If we want to support disks from other netbsd ports, then the 406 * code should also allow for a shorter label nearer the end of 407 * the disk sector, and (IIRC) labels within 8k of the disk start. 408 */ 409 dlp = (void *)a->bp->b_data; 410 if (action != WRITE_LABEL) { 411 dlp_lim = a->bp->b_data + a->lp->d_secsize - sizeof(*dlp); 412 for (;; dlp = (void *)((char *)dlp + sizeof(long))) { 413 if ((char *)dlp > dlp_lim) 414 return SCAN_CONTINUE; 415 if (dlp->d_magic != DISKMAGIC 416 || dlp->d_magic2 != DISKMAGIC) 417 continue; 418 if (dlp->d_npartitions > MAXPARTITIONS 419 || dkcksum(dlp) != 0) { 420 a->msg = "disk label corrupted"; 421 continue; 422 } 423 break; 424 } 425 } 426 427 switch (action) { 428 case READ_LABEL: 429 *a->lp = *dlp; 430 a->label_sector = label_sector; 431 return SCAN_FOUND; 432 case UPDATE_LABEL: 433 case WRITE_LABEL: 434 *dlp = *a->lp; 435 a->bp->b_flags &= ~(B_READ|B_DONE); 436 a->bp->b_flags |= B_WRITE; 437 (*a->strat)(a->bp); 438 error = biowait(a->bp); 439 if (error != 0) { 440 a->error = error; 441 a->msg = "disk label write failed"; 442 return SCAN_ERROR; 443 } 444 a->written++; 445 /* Write label to all mbr partitions */ 446 return SCAN_CONTINUE; 447 default: 448 return SCAN_ERROR; 449 } 450 } 451 452 /* 453 * Check new disk label for sensibility 454 * before setting it. 455 */ 456 int 457 setdisklabel(struct disklabel *olp, struct disklabel *nlp, u_long openmask, 458 struct cpu_disklabel *osdep) 459 { 460 int i; 461 struct partition *opp, *npp; 462 463 /* sanity clause */ 464 if (nlp->d_secpercyl == 0 || nlp->d_secsize == 0 465 || (nlp->d_secsize % DEV_BSIZE) != 0) 466 return (EINVAL); 467 468 /* special case to allow disklabel to be invalidated */ 469 if (nlp->d_magic == 0xffffffff) { 470 *olp = *nlp; 471 return (0); 472 } 473 474 if (nlp->d_magic != DISKMAGIC || nlp->d_magic2 != DISKMAGIC || 475 dkcksum(nlp) != 0) 476 return (EINVAL); 477 478 /* XXX missing check if other dos partitions will be overwritten */ 479 480 while (openmask != 0) { 481 i = ffs(openmask) - 1; 482 openmask &= ~(1 << i); 483 if (i > nlp->d_npartitions) 484 return (EBUSY); 485 opp = &olp->d_partitions[i]; 486 npp = &nlp->d_partitions[i]; 487 /* 488 * Copy internally-set partition information 489 * if new label doesn't include it. XXX 490 */ 491 if (npp->p_fstype == FS_UNUSED && opp->p_fstype != FS_UNUSED) { 492 *npp = *opp; 493 continue; 494 } 495 if (npp->p_offset != opp->p_offset || npp->p_size < opp->p_size) 496 return (EBUSY); 497 } 498 nlp->d_checksum = 0; 499 nlp->d_checksum = dkcksum(nlp); 500 *olp = *nlp; 501 return (0); 502 } 503 504 505 /* 506 * Write disk label back to device after modification. 507 */ 508 int 509 writedisklabel(dev_t dev, void (*strat)(struct buf *), struct disklabel *lp, 510 struct cpu_disklabel *osdep) 511 { 512 mbr_args_t a; 513 514 memset(&a, 0, sizeof a); 515 a.lp = lp; 516 a.strat = strat; 517 518 /* get a buffer and initialize it */ 519 a.bp = geteblk((int)lp->d_secsize); 520 a.bp->b_dev = dev; 521 522 if (osdep) 523 /* Write the label to every netbsd mbr partition */ 524 scan_mbr(&a, write_netbsd_label); 525 526 /* and overwrite any label at the start of the volume */ 527 validate_label(&a, LABELSECTOR, UPDATE_LABEL); 528 if (LABELSECTOR != 0) 529 validate_label(&a, 0, UPDATE_LABEL); 530 531 if (a.written == 0 && a.error == 0) 532 a.error = ESRCH; 533 534 brelse(a.bp); 535 return a.error; 536 } 537 538 static int 539 write_netbsd_label(mbr_args_t *a, mbr_partition_t *dp, int slot, uint ext_base) 540 { 541 int ptn_base = ext_base + le32toh(dp->mbrp_start); 542 543 if (dp->mbrp_type != MBR_PTYPE_NETBSD) 544 return SCAN_CONTINUE; 545 546 return validate_label(a, ptn_base + MBR_LABELSECTOR, WRITE_LABEL); 547 } 548 549 550 /* 551 * Determine the size of the transfer, and make sure it is 552 * within the boundaries of the partition. Adjust transfer 553 * if needed, and signal errors or early completion. 554 */ 555 int 556 bounds_check_with_label(struct disk *dk, struct buf *bp, int wlabel) 557 { 558 struct disklabel *lp = dk->dk_label; 559 struct partition *p = lp->d_partitions + DISKPART(bp->b_dev); 560 int labelsector = LABELSECTOR; 561 int64_t sz; 562 563 #if RAW_PART == 3 564 labelsector += lp->d_partitions[2].p_offset; 565 #endif 566 567 sz = howmany(bp->b_bcount, lp->d_secsize); 568 569 if (bp->b_blkno + sz > p->p_size) { 570 sz = p->p_size - bp->b_blkno; 571 if (sz == 0) { 572 /* If exactly at end of disk, return EOF. */ 573 bp->b_resid = bp->b_bcount; 574 return (0); 575 } 576 if (sz < 0) { 577 /* If past end of disk, return EINVAL. */ 578 bp->b_error = EINVAL; 579 goto bad; 580 } 581 /* Otherwise, truncate request. */ 582 bp->b_bcount = sz << DEV_BSHIFT; 583 } 584 585 /* Overwriting disk label? */ 586 if (bp->b_blkno + p->p_offset <= labelsector && 587 bp->b_blkno + p->p_offset + sz > labelsector && 588 (bp->b_flags & B_READ) == 0 && !wlabel) { 589 bp->b_error = EROFS; 590 goto bad; 591 } 592 593 /* calculate cylinder for disksort to order transfers with */ 594 bp->b_cylinder = (bp->b_blkno + p->p_offset) / 595 (lp->d_secsize / DEV_BSIZE) / lp->d_secpercyl; 596 return (1); 597 598 bad: 599 bp->b_flags |= B_ERROR; 600 return (-1); 601 } 602