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