1 /* $NetBSD: disksubr.c,v 1.2 2002/02/19 17:09:47 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 36 #include <sys/param.h> 37 #include <sys/systm.h> 38 #include <sys/buf.h> 39 #include <sys/disklabel.h> 40 41 #define NO_MBR_SIGNATURE ((struct mbr_partition *) -1) 42 43 static struct mbr_partition * 44 mbr_findslice(struct mbr_partition* dp, struct buf *bp); 45 46 /* 47 * Scan MBR for NetBSD partittion. Return NO_MBR_SIGNATURE if no MBR found 48 * Otherwise, copy valid MBR partition-table into dp, and if a NetBSD 49 * partition is found, return a pointer to it; else return NULL. 50 */ 51 static 52 struct mbr_partition * 53 mbr_findslice(struct mbr_partition *dp, struct buf *bp) 54 { 55 struct mbr_partition *ourdp = NULL; 56 u_int16_t *mbrmagicp; 57 int i; 58 59 /* Note: Magic number is little-endian. */ 60 mbrmagicp = (u_int16_t *)(bp->b_data + MBR_MAGICOFF); 61 if (*mbrmagicp != MBR_MAGIC) 62 return (NO_MBR_SIGNATURE); 63 64 /* XXX how do we check veracity/bounds of this? */ 65 memcpy(dp, bp->b_data + MBR_PARTOFF, NMBRPART * sizeof(*dp)); 66 67 /* look for NetBSD partition */ 68 for (i = 0; i < NMBRPART; i++) { 69 if (dp[i].mbrp_typ == MBR_PTYPE_NETBSD) { 70 ourdp = &dp[i]; 71 break; 72 } 73 } 74 75 return (ourdp); 76 } 77 78 79 /* 80 * Attempt to read a disk label from a device 81 * using the indicated strategy routine. 82 * The label must be partly set up before this: 83 * secpercyl, secsize and anything required for a block i/o read 84 * operation in the driver's strategy/start routines 85 * must be filled in before calling us. 86 * 87 * If dos partition table requested, attempt to load it and 88 * find disklabel inside a DOS partition. Also, if bad block 89 * table needed, attempt to extract it as well. Return buffer 90 * for use in signalling errors if requested. 91 * 92 * Returns null on success and an error string on failure. 93 */ 94 char * 95 readdisklabel(dev_t dev, void (*strat)(struct buf *), struct disklabel *lp, 96 struct cpu_disklabel *osdep) 97 { 98 struct mbr_partition *dp; 99 struct partition *pp; 100 struct dkbad *bdp; 101 struct buf *bp; 102 struct disklabel *dlp; 103 char *msg = NULL; 104 int dospartoff, cyl, i; 105 106 /* minimal requirements for archtypal disk label */ 107 if (lp->d_secsize == 0) 108 lp->d_secsize = DEV_BSIZE; 109 if (lp->d_secperunit == 0) 110 lp->d_secperunit = 0x1fffffff; 111 #if 0 112 if (lp->d_ncylinders == 16383) { 113 printf("disklabel: Disk > 8G ... readjusting chs %d/%d/%d to ", 114 lp->d_ncylinders, lp->d_ntracks, lp->d_nsectors); 115 lp->d_ncylinders = lp->d_secperunit / lp->d_ntracks / lp->d_nsectors; 116 printf("%d/%d/%d\n", 117 lp->d_ncylinders, lp->d_ntracks, lp->d_nsectors); 118 } 119 #endif 120 lp->d_npartitions = RAW_PART + 1; 121 for (i = 0; i < RAW_PART; i++) { 122 lp->d_partitions[i].p_size = 0; 123 lp->d_partitions[i].p_offset = 0; 124 } 125 if (lp->d_partitions[i].p_size == 0) 126 lp->d_partitions[i].p_size = 0x1fffffff; 127 lp->d_partitions[i].p_offset = 0; 128 129 /* get a buffer and initialize it */ 130 bp = geteblk((int)lp->d_secsize); 131 bp->b_dev = dev; 132 133 /* do dos partitions in the process of getting disklabel? */ 134 dospartoff = 0; 135 cyl = LABELSECTOR / lp->d_secpercyl; 136 if (!osdep) 137 goto nombrpart; 138 dp = osdep->dosparts; 139 140 /* read master boot record */ 141 bp->b_blkno = MBR_BBSECTOR; 142 bp->b_bcount = lp->d_secsize; 143 bp->b_flags |= B_READ; 144 bp->b_cylinder = MBR_BBSECTOR / lp->d_secpercyl; 145 (*strat)(bp); 146 147 /* if successful, wander through dos partition table */ 148 if (biowait(bp)) { 149 msg = "dos partition I/O error"; 150 goto done; 151 } else { 152 struct mbr_partition *ourdp = NULL; 153 154 ourdp = mbr_findslice(dp, bp); 155 if (ourdp == NO_MBR_SIGNATURE) 156 goto nombrpart; 157 158 for (i = 0; i < NMBRPART; i++, dp++) { 159 /* Install in partition e, f, g, or h. */ 160 pp = &lp->d_partitions[RAW_PART + 1 + i]; 161 pp->p_offset = dp->mbrp_start; 162 pp->p_size = dp->mbrp_size; 163 if (dp->mbrp_typ == MBR_PTYPE_LNXEXT2) 164 pp->p_fstype = FS_EX2FS; 165 166 if (dp->mbrp_typ == MBR_PTYPE_LNXSWAP) 167 pp->p_fstype = FS_SWAP; 168 169 /* is this ours? */ 170 if (dp == ourdp) { 171 /* need sector address for SCSI/IDE, 172 cylinder for ESDI/ST506/RLL */ 173 dospartoff = dp->mbrp_start; 174 cyl = MBR_PCYL(dp->mbrp_scyl, dp->mbrp_ssect); 175 176 /* update disklabel with details */ 177 lp->d_partitions[2].p_size = 178 dp->mbrp_size; 179 lp->d_partitions[2].p_offset = 180 dp->mbrp_start; 181 } 182 } 183 lp->d_npartitions = RAW_PART + 1 + i; 184 } 185 186 nombrpart: 187 /* next, dig out disk label */ 188 bp->b_blkno = dospartoff + LABELSECTOR; 189 bp->b_cylinder = cyl; 190 bp->b_bcount = lp->d_secsize; 191 bp->b_flags &= ~(B_DONE); 192 bp->b_flags |= B_READ; 193 (*strat)(bp); 194 195 /* if successful, locate disk label within block and validate */ 196 if (biowait(bp)) { 197 msg = "disk label I/O error"; 198 goto done; 199 } 200 for (dlp = (struct disklabel *)bp->b_data; 201 dlp <= (struct disklabel *)(bp->b_data + lp->d_secsize - sizeof(*dlp)); 202 dlp = (struct disklabel *)((char *)dlp + sizeof(long))) { 203 if (dlp->d_magic != DISKMAGIC || dlp->d_magic2 != DISKMAGIC) { 204 if (msg == NULL) 205 msg = "no disk label"; 206 } else if (dlp->d_npartitions > MAXPARTITIONS || 207 dkcksum(dlp) != 0) 208 msg = "disk label corrupted"; 209 else { 210 *lp = *dlp; 211 msg = NULL; 212 break; 213 } 214 } 215 216 if (msg) 217 goto done; 218 219 /* obtain bad sector table if requested and present */ 220 if (osdep && (lp->d_flags & D_BADSECT)) { 221 struct dkbad *db; 222 223 bdp = &osdep->bad; 224 i = 0; 225 do { 226 /* read a bad sector table */ 227 bp->b_flags &= ~(B_DONE); 228 bp->b_flags |= B_READ; 229 bp->b_blkno = lp->d_secperunit - lp->d_nsectors + i; 230 if (lp->d_secsize > DEV_BSIZE) 231 bp->b_blkno *= lp->d_secsize / DEV_BSIZE; 232 else 233 bp->b_blkno /= DEV_BSIZE / lp->d_secsize; 234 bp->b_bcount = lp->d_secsize; 235 bp->b_cylinder = lp->d_ncylinders - 1; 236 (*strat)(bp); 237 238 /* if successful, validate, otherwise try another */ 239 if (biowait(bp)) { 240 msg = "bad sector table I/O error"; 241 } else { 242 db = (struct dkbad *)(bp->b_data); 243 #define DKBAD_MAGIC 0x4321 244 if (db->bt_mbz == 0 245 && db->bt_flag == DKBAD_MAGIC) { 246 msg = NULL; 247 *bdp = *db; 248 break; 249 } else 250 msg = "bad sector table corrupted"; 251 } 252 } while ((bp->b_flags & B_ERROR) && (i += 2) < 10 && 253 i < lp->d_nsectors); 254 } 255 256 done: 257 brelse(bp); 258 return (msg); 259 } 260 261 /* 262 * Check new disk label for sensibility 263 * before setting it. 264 */ 265 int 266 setdisklabel(struct disklabel *olp, struct disklabel *nlp, u_long openmask, 267 struct cpu_disklabel *osdep) 268 { 269 int i; 270 struct partition *opp, *npp; 271 272 /* sanity clause */ 273 if (nlp->d_secpercyl == 0 || nlp->d_secsize == 0 274 || (nlp->d_secsize % DEV_BSIZE) != 0) 275 return(EINVAL); 276 277 /* special case to allow disklabel to be invalidated */ 278 if (nlp->d_magic == 0xffffffff) { 279 *olp = *nlp; 280 return (0); 281 } 282 283 if (nlp->d_magic != DISKMAGIC || nlp->d_magic2 != DISKMAGIC || 284 dkcksum(nlp) != 0) 285 return (EINVAL); 286 287 /* XXX missing check if other dos partitions will be overwritten */ 288 289 while (openmask != 0) { 290 i = ffs(openmask) - 1; 291 openmask &= ~(1 << i); 292 if (nlp->d_npartitions <= i) 293 return (EBUSY); 294 opp = &olp->d_partitions[i]; 295 npp = &nlp->d_partitions[i]; 296 if (npp->p_offset != opp->p_offset || npp->p_size < opp->p_size) 297 return (EBUSY); 298 /* 299 * Copy internally-set partition information 300 * if new label doesn't include it. XXX 301 */ 302 if (npp->p_fstype == FS_UNUSED && opp->p_fstype != FS_UNUSED) { 303 npp->p_fstype = opp->p_fstype; 304 npp->p_fsize = opp->p_fsize; 305 npp->p_frag = opp->p_frag; 306 npp->p_cpg = opp->p_cpg; 307 } 308 } 309 nlp->d_checksum = 0; 310 nlp->d_checksum = dkcksum(nlp); 311 *olp = *nlp; 312 return (0); 313 } 314 315 316 /* 317 * Write disk label back to device after modification. 318 */ 319 int 320 writedisklabel(dev_t dev, void (*strat)(struct buf *), struct disklabel *lp, 321 struct cpu_disklabel *osdep) 322 { 323 struct mbr_partition *dp; 324 struct buf *bp; 325 struct disklabel *dlp; 326 int error, dospartoff, cyl; 327 328 /* get a buffer and initialize it */ 329 bp = geteblk((int)lp->d_secsize); 330 bp->b_dev = dev; 331 332 /* do dos partitions in the process of getting disklabel? */ 333 dospartoff = 0; 334 cyl = LABELSECTOR / lp->d_secpercyl; 335 if (!osdep) 336 goto nombrpart; 337 dp = osdep->dosparts; 338 339 /* read master boot record */ 340 bp->b_blkno = MBR_BBSECTOR; 341 bp->b_bcount = lp->d_secsize; 342 bp->b_flags |= B_READ; 343 bp->b_cylinder = MBR_BBSECTOR / lp->d_secpercyl; 344 (*strat)(bp); 345 346 if ((error = biowait(bp)) == 0) { 347 struct mbr_partition *ourdp = NULL; 348 349 ourdp = mbr_findslice(dp, bp); 350 if (ourdp == NO_MBR_SIGNATURE) 351 goto nombrpart; 352 353 if (ourdp) { 354 /* need sector address for SCSI/IDE, 355 cylinder for ESDI/ST506/RLL */ 356 dospartoff = ourdp->mbrp_start; 357 cyl = MBR_PCYL(ourdp->mbrp_scyl, ourdp->mbrp_ssect); 358 } 359 } 360 361 nombrpart: 362 /* next, dig out disk label */ 363 bp->b_blkno = dospartoff + LABELSECTOR; 364 bp->b_cylinder = cyl; 365 bp->b_bcount = lp->d_secsize; 366 bp->b_flags &= ~(B_DONE); 367 bp->b_flags |= B_READ; 368 (*strat)(bp); 369 370 /* if successful, locate disk label within block and validate */ 371 if ((error = biowait(bp)) != 0) 372 goto done; 373 for (dlp = (struct disklabel *)bp->b_data; 374 dlp <= (struct disklabel *)(bp->b_data + lp->d_secsize - sizeof(*dlp)); 375 dlp = (struct disklabel *)((char *)dlp + sizeof(long))) { 376 if (dlp->d_magic == DISKMAGIC && dlp->d_magic2 == DISKMAGIC && 377 dkcksum(dlp) == 0) { 378 *dlp = *lp; 379 bp->b_flags &= ~(B_READ|B_DONE); 380 bp->b_flags |= B_WRITE; 381 (*strat)(bp); 382 error = biowait(bp); 383 goto done; 384 } 385 } 386 error = ESRCH; 387 388 done: 389 brelse(bp); 390 return (error); 391 } 392 393 /* 394 * Determine the size of the transfer, and make sure it is 395 * within the boundaries of the partition. Adjust transfer 396 * if needed, and signal errors or early completion. 397 */ 398 int 399 bounds_check_with_label(struct buf *bp, struct disklabel *lp, int wlabel) 400 { 401 struct partition *p = lp->d_partitions + DISKPART(bp->b_dev); 402 int labelsector = lp->d_partitions[2].p_offset + LABELSECTOR; 403 int sz; 404 405 sz = howmany(bp->b_bcount, lp->d_secsize); 406 407 if (bp->b_blkno + sz > p->p_size) { 408 sz = p->p_size - bp->b_blkno; 409 if (sz == 0) { 410 /* If exactly at end of disk, return EOF. */ 411 bp->b_resid = bp->b_bcount; 412 goto done; 413 } 414 if (sz < 0) { 415 /* If past end of disk, return EINVAL. */ 416 bp->b_error = EINVAL; 417 goto bad; 418 } 419 /* Otherwise, truncate request. */ 420 bp->b_bcount = sz << DEV_BSHIFT; 421 } 422 423 /* Overwriting disk label? */ 424 if (bp->b_blkno + p->p_offset <= labelsector && 425 #if LABELSECTOR != 0 426 bp->b_blkno + p->p_offset + sz > labelsector && 427 #endif 428 (bp->b_flags & B_READ) == 0 && !wlabel) { 429 bp->b_error = EROFS; 430 goto bad; 431 } 432 433 /* calculate cylinder for disksort to order transfers with */ 434 bp->b_cylinder = (bp->b_blkno + p->p_offset) / 435 (lp->d_secsize / DEV_BSIZE) / lp->d_secpercyl; 436 return (1); 437 438 bad: 439 bp->b_flags |= B_ERROR; 440 done: 441 return (0); 442 } 443