1/* $NetBSD: disksubr.c,v 1.56 2007/10/17 19:55:14 garbled 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 * Copyright (C) 1993 Allen K. Briggs, Chris P. Caputo, 35 * Michael L. Finch, Bradley A. Grantham, and 36 * Lawrence A. Kesteloot 37 * All rights reserved. 38 * 39 * Redistribution and use in source and binary forms, with or without 40 * modification, are permitted provided that the following conditions 41 * are met: 42 * 1. Redistributions of source code must retain the above copyright 43 * notice, this list of conditions and the following disclaimer. 44 * 2. Redistributions in binary form must reproduce the above copyright 45 * notice, this list of conditions and the following disclaimer in the 46 * documentation and/or other materials provided with the distribution. 47 * 3. All advertising materials mentioning features or use of this software 48 * must display the following acknowledgement: 49 * This product includes software developed by the Alice Group. 50 * 4. The names of the Alice Group or any of its members may not be used 51 * to endorse or promote products derived from this software without 52 * specific prior written permission. 53 * 54 * THIS SOFTWARE IS PROVIDED BY THE ALICE GROUP ``AS IS'' AND ANY EXPRESS OR 55 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 56 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 57 * IN NO EVENT SHALL THE ALICE GROUP BE LIABLE FOR ANY DIRECT, INDIRECT, 58 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 59 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 60 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 61 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 62 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 63 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 64 * 65 */ 66 67#include <sys/cdefs.h> 68__KERNEL_RCSID(0, "$NetBSD: disksubr.c,v 1.56 2007/10/17 19:55:14 garbled Exp $"); 69 70#include <sys/param.h> 71#include <sys/systm.h> 72#include <sys/buf.h> 73#include <sys/disk.h> 74#include <sys/disklabel.h> 75#include <sys/bootblock.h> 76#include <sys/syslog.h> 77 78#include <sys/bswap.h> 79 80#define NUM_PARTS 32 81 82#define ROOT_PART 1 83#define UFS_PART 2 84#define SWAP_PART 3 85#define HFS_PART 4 86#define SCRATCH_PART 5 87 88int fat_types[] = { 89 MBR_PTYPE_FAT12, MBR_PTYPE_FAT16S, 90 MBR_PTYPE_FAT16B, MBR_PTYPE_FAT32, 91 MBR_PTYPE_FAT32L, MBR_PTYPE_FAT16L, 92 -1 93}; 94 95static int getFreeLabelEntry(struct disklabel *); 96static int whichType(struct part_map_entry *); 97static void setpartition(struct part_map_entry *, struct partition *, int); 98static int getNamedType(struct part_map_entry *, int, struct disklabel *, int, 99 int, int *); 100static char *read_mac_label(char *, struct disklabel *, int *); 101static char *read_mbr_label(char *, struct disklabel *, int *); 102static const char *read_bsd_label(char *, struct disklabel *, int *); 103 104 105/* 106 * Find an entry in the disk label that is unused and return it 107 * or -1 if no entry 108 */ 109static int 110getFreeLabelEntry(struct disklabel *lp) 111{ 112 int i; 113 114 for (i = 0; i < MAXPARTITIONS; i++) { 115 if ((i != RAW_PART) 116 && (lp->d_partitions[i].p_fstype == FS_UNUSED)) 117 return i; 118 } 119 return -1; 120} 121 122/* 123 * figure out what the type of the given part is and return it 124 */ 125static int 126whichType(struct part_map_entry *part) 127{ 128 struct blockzeroblock *bzb; 129 char typestr[32], *s; 130 int type; 131 132 if (part->pmSig != PART_ENTRY_MAGIC || part->pmPartType[0] == '\0') 133 return 0; 134 135 strncpy(typestr, (char *)part->pmPartType, sizeof(typestr)); 136 typestr[sizeof(typestr) - 1] = '\0'; 137 for (s = typestr; *s; s++) 138 if ((*s >= 'a') && (*s <= 'z')) 139 *s = (*s - 'a' + 'A'); 140 141 if (strcmp(PART_TYPE_DRIVER, typestr) == 0 || 142 strcmp(PART_TYPE_DRIVER43, typestr) == 0 || 143 strcmp(PART_TYPE_DRIVERATA, typestr) == 0 || 144 strcmp(PART_TYPE_FWB_COMPONENT, typestr) == 0 || 145 strcmp(PART_TYPE_PARTMAP, typestr) == 0) 146 type = 0; 147 else if (strcmp(PART_TYPE_UNIX, typestr) == 0) { 148 /* unix part, swap, root, usr */ 149 bzb = (struct blockzeroblock *)(&part->pmBootArgs); 150 if (bzb->bzbMagic != BZB_MAGIC) 151 type = 0; 152 else if (bzb->bzbFlags & BZB_ROOTFS) 153 type = ROOT_PART; 154 else if (bzb->bzbFlags & BZB_USRFS) 155 type = UFS_PART; 156 else if (bzb->bzbType == BZB_TYPESWAP) 157 type = SWAP_PART; 158 else 159 type = SCRATCH_PART; 160 } else if (strcmp(PART_TYPE_MAC, typestr) == 0) 161 type = HFS_PART; 162 else 163 type = SCRATCH_PART; /* no known type */ 164 165 return type; 166} 167 168static void 169setpartition(struct part_map_entry *part, struct partition *pp, int fstype) 170{ 171 pp->p_size = part->pmPartBlkCnt; 172 pp->p_offset = part->pmPyPartStart; 173 pp->p_fstype = fstype; 174 175 part->pmPartType[0] = '\0'; 176} 177 178static int 179getNamedType(struct part_map_entry *part, int num_parts, struct disklabel *lp, 180 int type, int alt, int *maxslot) 181{ 182 struct blockzeroblock *bzb; 183 int i; 184 185 for (i = 0; i < num_parts; i++) { 186 if (whichType(part + i) != type) 187 continue; 188 189 if (type == ROOT_PART) { 190 bzb = (struct blockzeroblock *) 191 (&(part + i)->pmBootArgs); 192 if (alt >= 0 && alt != bzb->bzbCluster) 193 continue; 194 setpartition(part + i, &lp->d_partitions[0], FS_BSDFFS); 195 } else if (type == UFS_PART) { 196 bzb = (struct blockzeroblock *) 197 (&(part + i)->pmBootArgs); 198 if (alt >= 0 && alt != bzb->bzbCluster) 199 continue; 200 setpartition(part + i, &lp->d_partitions[6], FS_BSDFFS); 201 if (*maxslot < 6) 202 *maxslot = 6; 203 } else if (type == SWAP_PART) { 204 setpartition(part + i, &lp->d_partitions[1], FS_SWAP); 205 if (*maxslot < 1) 206 *maxslot = 1; 207 } else 208 printf("disksubr.c: can't do type %d\n", type); 209 210 return 0; 211 } 212 213 return -1; 214} 215 216/* 217 * MF -- 218 * here's what i'm gonna do: 219 * read in the entire diskpartition table, it may be bigger or smaller 220 * than NUM_PARTS but read that many entries. Each entry has a magic 221 * number so we'll know if an entry is crap. 222 * next fill in the disklabel with info like this 223 * next fill in the root, usr, and swap parts. 224 * then look for anything else and fit it in. 225 * A: root 226 * B: Swap 227 * C: Whole disk 228 * G: Usr 229 * 230 * 231 * I'm not entirely sure what netbsd386 wants in c & d 232 * 386bsd wants other stuff, so i'll leave them alone 233 * 234 * AKB -- I added to Mike's original algorithm by searching for a bzbCluster 235 * of zero for root, first. This allows A/UX to live on cluster 1 and 236 * NetBSD to live on cluster 0--regardless of the actual order on the 237 * disk. This whole algorithm should probably be changed in the future. 238 */ 239 240/* 241 * This uses sector zero. If this contains what looks like a valid 242 * Macintosh boot sector, we attempt to fill in the disklabel structure 243 * with the partition data from block #1 on. 244 */ 245static char * 246read_mac_label(char *dlbuf, struct disklabel *lp, int *match) 247{ 248 u_int16_t *sbSigp; 249 struct part_map_entry *part; 250 struct partition *pp; 251 char *msg; 252 int i, slot, maxslot; 253 254 maxslot = 0; 255 *match = 0; 256 msg = NULL; 257 258 sbSigp = (u_int16_t *)dlbuf; 259 if (*sbSigp != DRIVER_MAP_MAGIC) 260 return msg; 261 262 /* Found Macintosh partition magic number; set up disklabel */ 263 *match = (-1); 264 265 /* the Macintosh partition table starts at sector #1 */ 266 part = (struct part_map_entry *)(dlbuf + DEV_BSIZE); 267 268 /* Fill in standard partitions */ 269 lp->d_npartitions = RAW_PART + 1; 270 if (getNamedType(part, NUM_PARTS, lp, ROOT_PART, 0, &maxslot)) 271 getNamedType(part, NUM_PARTS, lp, ROOT_PART, -1, &maxslot); 272 if (getNamedType(part, NUM_PARTS, lp, UFS_PART, 0, &maxslot)) 273 getNamedType(part, NUM_PARTS, lp, UFS_PART, -1, &maxslot); 274 getNamedType(part, NUM_PARTS, lp, SWAP_PART, -1, &maxslot); 275 276 /* Now get as many of the rest of the partitions as we can */ 277 for (i = 0; i < NUM_PARTS; i++) { 278 slot = getFreeLabelEntry(lp); 279 if (slot < 0) 280 break; 281 282 pp = &lp->d_partitions[slot]; 283 284 switch (whichType(part + i)) { 285 case ROOT_PART: 286 /* 287 * another root part will turn into a plain old 288 * UFS_PART partition, live with it. 289 */ 290 case UFS_PART: 291 setpartition(part + i, pp, FS_BSDFFS); 292 break; 293 case SWAP_PART: 294 setpartition(part + i, pp, FS_SWAP); 295 break; 296 case HFS_PART: 297 setpartition(part + i, pp, FS_HFS); 298 break; 299 case SCRATCH_PART: 300 setpartition(part + i, pp, FS_OTHER); 301 break; 302 default: 303 slot = 0; 304 break; 305 } 306 if (slot > maxslot) 307 maxslot = slot; 308 } 309 lp->d_npartitions = ((maxslot >= RAW_PART) ? maxslot : RAW_PART) + 1; 310 return msg; 311} 312 313/* 314 * Scan the disk buffer for a DOS style master boot record. 315 * Return if no match; otherwise, set up an in-core disklabel . 316 * 317 * XXX stuff like this really should be MI 318 * 319 * Since FFS is endian sensitive, we pay no effort in attempting to 320 * dig up *BSD/i386 disk labels that may be present on the disk. 321 * Hence anything but DOS partitions is treated as unknown FS type, but 322 * this should suffice to mount_msdos Zip and other removable media. 323 */ 324static char * 325read_mbr_label(char *dlbuf, struct disklabel *lp, int *match) 326{ 327 struct mbr_partition *dp; 328 struct partition *pp; 329 char *msg; 330 size_t mbr_lbl_off; 331 int i, *ip, slot, maxslot; 332 333 maxslot = 0; 334 *match = 0; 335 msg = NULL; 336 337 if (MBR_MAGIC != bswap16(*(u_int16_t *)(dlbuf + MBR_MAGIC_OFFSET))) 338 return msg; 339 340 /* Found MBR magic number; set up disklabel */ 341 *match = (-1); 342 mbr_lbl_off = MBR_BBSECTOR * lp->d_secsize + MBR_PART_OFFSET; 343 344 dp = (struct mbr_partition *)(dlbuf + mbr_lbl_off); 345 for (i = 0; i < MBR_PART_COUNT; i++, dp++) { 346 if (dp->mbrp_type == 0) 347 continue; 348 349 slot = getFreeLabelEntry(lp); 350 maxslot = (slot > maxslot) ? maxslot : slot; 351 352 pp = &lp->d_partitions[slot]; 353 pp->p_fstype = FS_OTHER; 354 pp->p_offset = bswap32(dp->mbrp_start); 355 pp->p_size = bswap32(dp->mbrp_size); 356 357 for (ip = fat_types; *ip != -1; ip++) { 358 if (dp->mbrp_type == *ip) { 359 pp->p_fstype = FS_MSDOS; 360 break; 361 } 362 } 363 } 364 lp->d_npartitions = ((maxslot >= RAW_PART) ? maxslot : RAW_PART) + 1; 365 return msg; 366} 367 368/* 369 * Scan the disk buffer in four byte steps for a native BSD disklabel 370 * (different ports have variable-sized bootcode before the label) 371 */ 372static const char * 373read_bsd_label(char *dlbuf, struct disklabel *lp, int *match) 374{ 375 struct disklabel *dlp; 376 const char *msg; 377 struct disklabel *blk_start, *blk_end; 378 379 *match = 0; 380 msg = NULL; 381 382 blk_start = (struct disklabel *)dlbuf; 383 blk_end = (struct disklabel *)(dlbuf + (NUM_PARTS << DEV_BSHIFT) - 384 sizeof(struct disklabel)); 385 386 for (dlp = blk_start; dlp <= blk_end; 387 dlp = (struct disklabel *)((char *)dlp + sizeof(long))) { 388 if (dlp->d_magic == DISKMAGIC && dlp->d_magic2 == DISKMAGIC) { 389 /* Sanity check */ 390 if (dlp->d_npartitions <= MAXPARTITIONS && 391 dkcksum(dlp) == 0) { 392 *lp = *dlp; 393 *match = (-1); 394 } else 395 msg = "Disk label corrupted"; 396 break; 397 } 398 } 399 return msg; 400} 401 402/* 403 * Attempt to read a disk label from a device using the indicated strategy 404 * routine. The label must be partly set up before this: secpercyl and 405 * anything required in the strategy routine (e.g., sector size) must be 406 * filled in before calling us. Returns null on success and an error 407 * string on failure. 408 */ 409const char * 410readdisklabel(dev_t dev, void (*strat)(struct buf *), struct disklabel *lp, 411 struct cpu_disklabel *osdep) 412{ 413 struct buf *bp; 414 const char *msg; 415 int size; 416 417 if (lp->d_secperunit == 0) 418 lp->d_secperunit = 0x1fffffff; 419 420 if (lp->d_secpercyl == 0) 421 return msg = "Zero secpercyl"; 422 423 msg = NULL; 424 425 /* 426 * Read in the first #(NUM_PARTS + 1) blocks of the disk. 427 * The native Macintosh partition table starts at 428 * sector #1, but we want #0 too for the BSD label. 429 */ 430 431 size = roundup((NUM_PARTS + 1) << DEV_BSHIFT, lp->d_secsize); 432 bp = geteblk(size); 433 434 bp->b_dev = dev; 435 bp->b_blkno = 0; 436 bp->b_resid = 0; 437 bp->b_bcount = size; 438 bp->b_flags |= B_READ; 439 bp->b_cylinder = 1 / lp->d_secpercyl; 440 (*strat)(bp); 441 442 if (biowait(bp)) { 443 msg = "I/O error reading block zero"; 444 } else { 445 int match; 446 447 /* Add any offsets in the table handlers */ 448 msg = read_mac_label(bp->b_data, lp, &match); 449 if (!match && msg == NULL) 450 msg = read_mbr_label(bp->b_data, lp, &match); 451 if (!match && msg == NULL) 452 msg = read_bsd_label(bp->b_data, lp, &match); 453 if (!match && msg == NULL) 454 msg = "no disk label"; 455 } 456 457 brelse(bp, 0); 458 return (msg); 459} 460 461/* 462 * Check new disk label for sensibility before setting it. 463 */ 464int 465setdisklabel(struct disklabel *olp, struct disklabel *nlp, u_long openmask, 466 struct cpu_disklabel *osdep) 467{ 468#if 0 469 int i; 470 struct partition *opp, *npp; 471 472 /* sanity clause */ 473 if (nlp->d_secpercyl == 0 || nlp->d_secsize == 0 || 474 (nlp->d_secsize % DEV_BSIZE) != 0) 475 return(EINVAL); 476 477 /* special case to allow disklabel to be invalidated */ 478 if (nlp->d_magic == 0xffffffff) { 479 *olp = *nlp; 480 return (0); 481 } 482 483 if (nlp->d_magic != DISKMAGIC || nlp->d_magic2 != DISKMAGIC || 484 dkcksum(nlp) != 0) 485 return (EINVAL); 486 487 /* 488 * XXX We are missing any sort of check if other partition types, 489 * e.g. Macintosh or (PC) BIOS, will be overwritten. 490 */ 491 492 while ((i = ffs(openmask)) != 0) { 493 i--; 494 openmask &= ~(1 << i); 495 if (nlp->d_npartitions <= i) 496 return (EBUSY); 497 opp = &olp->d_partitions[i]; 498 npp = &nlp->d_partitions[i]; 499 if (npp->p_offset != opp->p_offset || npp->p_size < opp->p_size) 500 return (EBUSY); 501 /* 502 * Copy internally-set partition information 503 * if new label doesn't include it. XXX 504 */ 505 if (npp->p_fstype == FS_UNUSED && opp->p_fstype != FS_UNUSED) { 506 npp->p_fstype = opp->p_fstype; 507 npp->p_fsize = opp->p_fsize; 508 npp->p_frag = opp->p_frag; 509 npp->p_cpg = opp->p_cpg; 510 } 511 } 512 nlp->d_checksum = 0; 513 nlp->d_checksum = dkcksum(nlp); 514 *olp = *nlp; 515#endif 516 return (0); 517} 518 519/* 520 * Write disk label back to device after modification. 521 * 522 * MF - 8-14-93 This function is never called. It is here just in case 523 * we want to write dos disklabels some day. Really! 524 */ 525int 526writedisklabel(dev_t dev, void (*strat)(struct buf *), struct disklabel *lp, 527 struct cpu_disklabel *osdep) 528{ 529#if 0 530 struct buf *bp; 531 struct disklabel *dlp; 532 int labelpart; 533 int error = 0; 534 535 labelpart = DISKPART(dev); 536 if (lp->d_partitions[labelpart].p_offset != 0) { 537 if (lp->d_partitions[0].p_offset != 0) 538 return (EXDEV); /* not quite right */ 539 labelpart = 0; 540 } 541 bp = geteblk((int)lp->d_secsize); 542 bp->b_dev = MAKEDISKDEV(major(dev), DISKUNIT(dev), labelpart); 543 bp->b_blkno = LABELSECTOR; 544 bp->b_bcount = lp->d_secsize; 545 bp->b_flags |= B_READ; 546 (*strat)(bp); 547 if (error = biowait(bp)) 548 goto done; 549 for (dlp = (struct disklabel *)bp->b_data; 550 dlp <= (struct disklabel *) 551 ((char *)bp->b_data + lp->d_secsize - sizeof(*dlp)); 552 dlp = (struct disklabel *)((char *)dlp + sizeof(long))) { 553 if (dlp->d_magic == DISKMAGIC && dlp->d_magic2 == DISKMAGIC && 554 dkcksum(dlp) == 0) { 555 *dlp = *lp; 556 bp->b_oflags &= ~(BO_DONE); 557 bp->b_flags &= ~(B_READ); 558 bp->b_flags |= B_WRITE; 559 (*strat)(bp); 560 error = biowait(bp); 561 goto done; 562 } 563 } 564 error = ESRCH; 565done: 566 brelse(bp, 0); 567 return (error); 568#else 569 int i; 570 571 /* 572 * Clear and re-analyze the ondisk Apple Disk Partition Map, 573 * then recompute the faked incore disk label. This is necessary 574 * for sysinst, which may have modified the disk layout. We don't 575 * (yet?) support writing real BSD disk labels, so this hack 576 * instead causes the DIOCWDINFO ioctl invoked by sysinst to 577 * update the in-core disk label when it is "written" to disk. 578 * This code was originally developed by Bob Nestor on 9/13/99. 579 */ 580 lp->d_npartitions = 0; 581 for (i = 0; i < MAXPARTITIONS; i++) { 582 lp->d_partitions[i].p_fstype = FS_UNUSED; 583 lp->d_partitions[i].p_offset = 0; 584 if (i != RAW_PART) 585 lp->d_partitions[i].p_size = 0; 586 } 587 return (readdisklabel(dev, strat, lp, osdep) ? EINVAL : 0); 588#endif 589} 590