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