1 /* $NetBSD: disksubr.c,v 1.3 2002/02/19 17:09:43 wiz 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. All advertising materials mentioning features or use of this software 16 * must display the following acknowledgement: 17 * This product includes software developed by the University of 18 * California, Berkeley and its contributors. 19 * 4. Neither the name of the University nor the names of its contributors 20 * may be used to endorse or promote products derived from this software 21 * without specific prior written permission. 22 * 23 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 24 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 25 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 26 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 27 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 28 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 29 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 30 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 31 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 32 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 33 * SUCH DAMAGE. 34 * 35 * @(#)ufs_disksubr.c 7.16 (Berkeley) 5/4/91 36 */ 37 38 #include <sys/param.h> 39 #include <sys/systm.h> 40 #include <sys/buf.h> 41 #include <sys/disklabel.h> 42 #include <sys/disk.h> 43 #include <sys/syslog.h> 44 45 #include "opt_mbr.h" 46 47 int fat_types[] = { MBR_PTYPE_FAT12, MBR_PTYPE_FAT16S, 48 MBR_PTYPE_FAT16B, MBR_PTYPE_FAT32, 49 MBR_PTYPE_FAT32L, MBR_PTYPE_FAT16L, 50 -1 }; 51 52 #define NO_MBR_SIGNATURE ((struct mbr_partition *) -1) 53 54 static struct mbr_partition * 55 mbr_findslice(struct mbr_partition* dp, struct buf *bp); 56 57 /* 58 * Scan MBR for NetBSD partittion. Return NO_MBR_SIGNATURE if no MBR found 59 * Otherwise, copy valid MBR partition-table into dp, and if a NetBSD 60 * partition is found, return a pointer to it; else return NULL. 61 */ 62 static 63 struct mbr_partition * 64 mbr_findslice(struct mbr_partition *dp, struct buf *bp) 65 { 66 struct mbr_partition *ourdp = NULL; 67 u_int16_t *mbrmagicp; 68 int i; 69 70 /* Note: Magic number is little-endian. */ 71 mbrmagicp = (u_int16_t *)(bp->b_data + MBR_MAGICOFF); 72 if (*mbrmagicp != MBR_MAGIC) 73 return (NO_MBR_SIGNATURE); 74 75 /* XXX how do we check veracity/bounds of this? */ 76 memcpy(dp, bp->b_data + MBR_PARTOFF, NMBRPART * sizeof(*dp)); 77 78 /* look for NetBSD partition */ 79 for (i = 0; i < NMBRPART; i++) { 80 if (dp[i].mbrp_typ == MBR_PTYPE_NETBSD) { 81 ourdp = &dp[i]; 82 break; 83 } 84 } 85 86 #ifdef COMPAT_386BSD_MBRPART 87 /* didn't find it -- look for 386BSD partition */ 88 if (!ourdp) { 89 for (i = 0; i < NMBRPART; i++) { 90 if (dp[i].mbrp_typ == MBR_PTYPE_386BSD) { 91 printf("WARNING: old BSD partition ID!\n"); 92 ourdp = &dp[i]; 93 /* 94 * If more than one matches, take last, 95 * as NetBSD install tool does. 96 */ 97 #if 0 98 break; 99 #endif 100 } 101 } 102 } 103 #endif /* COMPAT_386BSD_MBRPART */ 104 105 return (ourdp); 106 } 107 108 109 /* 110 * Attempt to read a disk label from a device 111 * using the indicated strategy routine. 112 * The label must be partly set up before this: 113 * secpercyl, secsize and anything required for a block i/o read 114 * operation in the driver's strategy/start routines 115 * must be filled in before calling us. 116 * 117 * If dos partition table requested, attempt to load it and 118 * find disklabel inside a DOS partition. Also, if bad block 119 * table needed, attempt to extract it as well. Return buffer 120 * for use in signalling errors if requested. 121 * 122 * Returns null on success and an error string on failure. 123 */ 124 char * 125 readdisklabel(dev_t dev, void (*strat)(struct buf *), struct disklabel *lp, 126 struct cpu_disklabel *osdep) 127 { 128 struct mbr_partition *dp; 129 struct partition *pp; 130 struct dkbad *bdp; 131 struct buf *bp; 132 struct disklabel *dlp; 133 char *msg = NULL; 134 int dospartoff, cyl, i, *ip; 135 136 /* minimal requirements for archtypal disk label */ 137 if (lp->d_secsize == 0) 138 lp->d_secsize = DEV_BSIZE; 139 if (lp->d_secperunit == 0) 140 lp->d_secperunit = 0x1fffffff; 141 #if 0 142 if (lp->d_ncylinders == 16383) { 143 printf("disklabel: Disk > 8G ... readjusting chs %d/%d/%d to ", 144 lp->d_ncylinders, lp->d_ntracks, lp->d_nsectors); 145 lp->d_ncylinders = lp->d_secperunit / lp->d_ntracks / lp->d_nsectors; 146 printf("%d/%d/%d\n", 147 lp->d_ncylinders, lp->d_ntracks, lp->d_nsectors); 148 } 149 #endif 150 lp->d_npartitions = RAW_PART + 1; 151 for (i = 0; i < RAW_PART; i++) { 152 lp->d_partitions[i].p_size = 0; 153 lp->d_partitions[i].p_offset = 0; 154 } 155 if (lp->d_partitions[i].p_size == 0) 156 lp->d_partitions[i].p_size = 0x1fffffff; 157 lp->d_partitions[i].p_offset = 0; 158 159 /* get a buffer and initialize it */ 160 bp = geteblk((int)lp->d_secsize); 161 bp->b_dev = dev; 162 163 /* do dos partitions in the process of getting disklabel? */ 164 dospartoff = 0; 165 cyl = LABELSECTOR / lp->d_secpercyl; 166 if (!osdep) 167 goto nombrpart; 168 dp = osdep->dosparts; 169 170 /* read master boot record */ 171 bp->b_blkno = MBR_BBSECTOR; 172 bp->b_bcount = lp->d_secsize; 173 bp->b_flags |= B_READ; 174 bp->b_cylinder = MBR_BBSECTOR / lp->d_secpercyl; 175 (*strat)(bp); 176 177 /* if successful, wander through dos partition table */ 178 if (biowait(bp)) { 179 msg = "dos partition I/O error"; 180 goto done; 181 } else { 182 struct mbr_partition *ourdp = NULL; 183 184 ourdp = mbr_findslice(dp, bp); 185 if (ourdp == NO_MBR_SIGNATURE) 186 goto nombrpart; 187 188 for (i = 0; i < NMBRPART; i++, dp++) { 189 /* Install in partition e, f, g, or h. */ 190 pp = &lp->d_partitions[RAW_PART + 1 + i]; 191 pp->p_offset = dp->mbrp_start; 192 pp->p_size = dp->mbrp_size; 193 for (ip = fat_types; *ip != -1; ip++) { 194 if (dp->mbrp_typ == *ip) 195 pp->p_fstype = FS_MSDOS; 196 } 197 if (dp->mbrp_typ == MBR_PTYPE_LNXEXT2) 198 pp->p_fstype = FS_EX2FS; 199 200 if (dp->mbrp_typ == MBR_PTYPE_NTFS) 201 pp->p_fstype = FS_NTFS; 202 203 /* is this ours? */ 204 if (dp == ourdp) { 205 /* need sector address for SCSI/IDE, 206 cylinder for ESDI/ST506/RLL */ 207 dospartoff = dp->mbrp_start; 208 cyl = MBR_PCYL(dp->mbrp_scyl, dp->mbrp_ssect); 209 210 /* update disklabel with details */ 211 lp->d_partitions[2].p_size = 212 dp->mbrp_size; 213 lp->d_partitions[2].p_offset = 214 dp->mbrp_start; 215 #if 0 216 if (lp->d_ntracks != dp->mbrp_ehd + 1 || 217 lp->d_nsectors != DPSECT(dp->mbrp_esect)) { 218 printf("disklabel: BIOS sees chs %d/%d/%d as ", 219 lp->d_ncylinders, lp->d_ntracks, 220 lp->d_nsectors); 221 lp->d_ntracks = dp->mbrp_ehd + 1; 222 lp->d_nsectors = DPSECT(dp->mbrp_esect); 223 lp->d_secpercyl = lp->d_ntracks * lp->d_nsectors; 224 lp->d_ncylinders = lp->d_secperunit / lp->d_secpercyl; 225 if (! lp->d_ncylinders) 226 lp->d_ncylinders = 1; 227 printf("%d/%d/%d\n", 228 lp->d_ncylinders, lp->d_ntracks, 229 lp->d_nsectors); 230 } 231 #endif 232 } 233 } 234 lp->d_npartitions = RAW_PART + 1 + i; 235 } 236 237 nombrpart: 238 /* next, dig out disk label */ 239 bp->b_blkno = dospartoff + LABELSECTOR; 240 bp->b_cylinder = cyl; 241 bp->b_bcount = lp->d_secsize; 242 bp->b_flags &= ~(B_DONE); 243 bp->b_flags |= B_READ; 244 (*strat)(bp); 245 246 /* if successful, locate disk label within block and validate */ 247 if (biowait(bp)) { 248 msg = "disk label I/O error"; 249 goto done; 250 } 251 for (dlp = (struct disklabel *)bp->b_data; 252 dlp <= (struct disklabel *)(bp->b_data + lp->d_secsize - sizeof(*dlp)); 253 dlp = (struct disklabel *)((char *)dlp + sizeof(long))) { 254 if (dlp->d_magic != DISKMAGIC || dlp->d_magic2 != DISKMAGIC) { 255 if (msg == NULL) 256 msg = "no disk label"; 257 } else if (dlp->d_npartitions > MAXPARTITIONS || 258 dkcksum(dlp) != 0) 259 msg = "disk label corrupted"; 260 else { 261 *lp = *dlp; 262 msg = NULL; 263 break; 264 } 265 } 266 267 if (msg) 268 goto done; 269 270 /* obtain bad sector table if requested and present */ 271 if (osdep && (lp->d_flags & D_BADSECT)) { 272 struct dkbad *db; 273 274 bdp = &osdep->bad; 275 i = 0; 276 do { 277 /* read a bad sector table */ 278 bp->b_flags &= ~(B_DONE); 279 bp->b_flags |= B_READ; 280 bp->b_blkno = lp->d_secperunit - lp->d_nsectors + i; 281 if (lp->d_secsize > DEV_BSIZE) 282 bp->b_blkno *= lp->d_secsize / DEV_BSIZE; 283 else 284 bp->b_blkno /= DEV_BSIZE / lp->d_secsize; 285 bp->b_bcount = lp->d_secsize; 286 bp->b_cylinder = lp->d_ncylinders - 1; 287 (*strat)(bp); 288 289 /* if successful, validate, otherwise try another */ 290 if (biowait(bp)) { 291 msg = "bad sector table I/O error"; 292 } else { 293 db = (struct dkbad *)(bp->b_data); 294 #define DKBAD_MAGIC 0x4321 295 if (db->bt_mbz == 0 296 && db->bt_flag == DKBAD_MAGIC) { 297 msg = NULL; 298 *bdp = *db; 299 break; 300 } else 301 msg = "bad sector table corrupted"; 302 } 303 } while ((bp->b_flags & B_ERROR) && (i += 2) < 10 && 304 i < lp->d_nsectors); 305 } 306 307 done: 308 brelse(bp); 309 return (msg); 310 } 311 312 /* 313 * Check new disk label for sensibility 314 * before setting it. 315 */ 316 int 317 setdisklabel(struct disklabel *olp, struct disklabel *nlp, u_long openmask, 318 struct cpu_disklabel *osdep) 319 { 320 int i; 321 struct partition *opp, *npp; 322 323 /* sanity clause */ 324 if (nlp->d_secpercyl == 0 || nlp->d_secsize == 0 325 || (nlp->d_secsize % DEV_BSIZE) != 0) 326 return(EINVAL); 327 328 /* special case to allow disklabel to be invalidated */ 329 if (nlp->d_magic == 0xffffffff) { 330 *olp = *nlp; 331 return (0); 332 } 333 334 if (nlp->d_magic != DISKMAGIC || nlp->d_magic2 != DISKMAGIC || 335 dkcksum(nlp) != 0) 336 return (EINVAL); 337 338 /* XXX missing check if other dos partitions will be overwritten */ 339 340 while (openmask != 0) { 341 i = ffs(openmask) - 1; 342 openmask &= ~(1 << i); 343 if (nlp->d_npartitions <= i) 344 return (EBUSY); 345 opp = &olp->d_partitions[i]; 346 npp = &nlp->d_partitions[i]; 347 if (npp->p_offset != opp->p_offset || npp->p_size < opp->p_size) 348 return (EBUSY); 349 /* 350 * Copy internally-set partition information 351 * if new label doesn't include it. XXX 352 */ 353 if (npp->p_fstype == FS_UNUSED && opp->p_fstype != FS_UNUSED) { 354 npp->p_fstype = opp->p_fstype; 355 npp->p_fsize = opp->p_fsize; 356 npp->p_frag = opp->p_frag; 357 npp->p_cpg = opp->p_cpg; 358 } 359 } 360 nlp->d_checksum = 0; 361 nlp->d_checksum = dkcksum(nlp); 362 *olp = *nlp; 363 return (0); 364 } 365 366 367 /* 368 * Write disk label back to device after modification. 369 */ 370 int 371 writedisklabel(dev_t dev, void (*strat)(struct buf *), struct disklabel *lp, 372 struct cpu_disklabel *osdep) 373 { 374 struct mbr_partition *dp; 375 struct buf *bp; 376 struct disklabel *dlp; 377 int error, dospartoff, cyl; 378 379 /* get a buffer and initialize it */ 380 bp = geteblk((int)lp->d_secsize); 381 bp->b_dev = dev; 382 383 /* do dos partitions in the process of getting disklabel? */ 384 dospartoff = 0; 385 cyl = LABELSECTOR / lp->d_secpercyl; 386 if (!osdep) 387 goto nombrpart; 388 dp = osdep->dosparts; 389 390 /* read master boot record */ 391 bp->b_blkno = MBR_BBSECTOR; 392 bp->b_bcount = lp->d_secsize; 393 bp->b_flags |= B_READ; 394 bp->b_cylinder = MBR_BBSECTOR / lp->d_secpercyl; 395 (*strat)(bp); 396 397 if ((error = biowait(bp)) == 0) { 398 struct mbr_partition *ourdp = NULL; 399 400 ourdp = mbr_findslice(dp, bp); 401 if (ourdp == NO_MBR_SIGNATURE) 402 goto nombrpart; 403 404 if (ourdp) { 405 /* need sector address for SCSI/IDE, 406 cylinder for ESDI/ST506/RLL */ 407 dospartoff = ourdp->mbrp_start; 408 cyl = MBR_PCYL(ourdp->mbrp_scyl, ourdp->mbrp_ssect); 409 } 410 } 411 412 nombrpart: 413 #ifdef maybe 414 /* disklabel in appropriate location? */ 415 if (lp->d_partitions[2].p_offset != 0 416 && lp->d_partitions[2].p_offset != dospartoff) { 417 error = EXDEV; 418 goto done; 419 } 420 #endif 421 422 /* next, dig out disk label */ 423 bp->b_blkno = dospartoff + LABELSECTOR; 424 bp->b_cylinder = cyl; 425 bp->b_bcount = lp->d_secsize; 426 bp->b_flags &= ~(B_DONE); 427 bp->b_flags |= B_READ; 428 (*strat)(bp); 429 430 /* if successful, locate disk label within block and validate */ 431 if ((error = biowait(bp)) != 0) 432 goto done; 433 for (dlp = (struct disklabel *)bp->b_data; 434 dlp <= (struct disklabel *)(bp->b_data + lp->d_secsize - sizeof(*dlp)); 435 dlp = (struct disklabel *)((char *)dlp + sizeof(long))) { 436 if (dlp->d_magic == DISKMAGIC && dlp->d_magic2 == DISKMAGIC && 437 dkcksum(dlp) == 0) { 438 *dlp = *lp; 439 bp->b_flags &= ~(B_READ|B_DONE); 440 bp->b_flags |= B_WRITE; 441 (*strat)(bp); 442 error = biowait(bp); 443 goto done; 444 } 445 } 446 error = ESRCH; 447 448 done: 449 brelse(bp); 450 return (error); 451 } 452 453 /* 454 * Determine the size of the transfer, and make sure it is 455 * within the boundaries of the partition. Adjust transfer 456 * if needed, and signal errors or early completion. 457 */ 458 int 459 bounds_check_with_label(struct buf *bp, struct disklabel *lp, int wlabel) 460 { 461 struct partition *p = lp->d_partitions + DISKPART(bp->b_dev); 462 int labelsector = lp->d_partitions[2].p_offset + LABELSECTOR; 463 int sz; 464 465 sz = howmany(bp->b_bcount, lp->d_secsize); 466 467 if (bp->b_blkno + sz > p->p_size) { 468 sz = p->p_size - bp->b_blkno; 469 if (sz == 0) { 470 /* If exactly at end of disk, return EOF. */ 471 bp->b_resid = bp->b_bcount; 472 goto done; 473 } 474 if (sz < 0) { 475 /* If past end of disk, return EINVAL. */ 476 bp->b_error = EINVAL; 477 goto bad; 478 } 479 /* Otherwise, truncate request. */ 480 bp->b_bcount = sz << DEV_BSHIFT; 481 } 482 483 /* Overwriting disk label? */ 484 if (bp->b_blkno + p->p_offset <= labelsector && 485 #if LABELSECTOR != 0 486 bp->b_blkno + p->p_offset + sz > labelsector && 487 #endif 488 (bp->b_flags & B_READ) == 0 && !wlabel) { 489 bp->b_error = EROFS; 490 goto bad; 491 } 492 493 /* calculate cylinder for disksort to order transfers with */ 494 bp->b_cylinder = (bp->b_blkno + p->p_offset) / 495 (lp->d_secsize / DEV_BSIZE) / lp->d_secpercyl; 496 return (1); 497 498 bad: 499 bp->b_flags |= B_ERROR; 500 done: 501 return (0); 502 } 503