1 /* $NetBSD: disksubr.c,v 1.20 2009/03/16 23:11:15 dsl 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 32 #include <sys/cdefs.h> 33 __KERNEL_RCSID(0, "$NetBSD: disksubr.c,v 1.20 2009/03/16 23:11:15 dsl Exp $"); 34 35 #include <sys/param.h> 36 #include <sys/systm.h> 37 #include <sys/buf.h> 38 #include <sys/disklabel.h> 39 #include <sys/disk.h> 40 #include <sys/syslog.h> 41 42 #define NO_MBR_SIGNATURE ((struct mbr_partition *) -1) 43 44 static struct mbr_partition * 45 mbr_findslice(struct mbr_partition* dp, struct buf *bp); 46 47 /* 48 * Scan MBR for NetBSD partittion. Return NO_MBR_SIGNATURE if no MBR found 49 * Otherwise, copy valid MBR partition-table into dp, and if a NetBSD 50 * partition is found, return a pointer to it; else return NULL. 51 */ 52 static 53 struct mbr_partition * 54 mbr_findslice(struct mbr_partition *dp, struct buf *bp) 55 { 56 struct mbr_partition *ourdp = NULL; 57 uint16_t *mbrmagicp; 58 int i; 59 60 /* Note: Magic number is little-endian. */ 61 mbrmagicp = (uint16_t *)((char *)bp->b_data + MBR_MAGIC_OFFSET); 62 if (*mbrmagicp != MBR_MAGIC) 63 return (NO_MBR_SIGNATURE); 64 65 /* XXX how do we check veracity/bounds of this? */ 66 memcpy(dp, (char *)bp->b_data + MBR_PART_OFFSET, MBR_PART_COUNT * sizeof(*dp)); 67 68 /* look for NetBSD partition */ 69 for (i = 0; i < MBR_PART_COUNT; i++) { 70 if (dp[i].mbrp_type == MBR_PTYPE_NETBSD) { 71 ourdp = &dp[i]; 72 break; 73 } 74 } 75 76 return (ourdp); 77 } 78 79 80 /* 81 * Attempt to read a disk label from a device 82 * using the indicated strategy routine. 83 * The label must be partly set up before this: 84 * secpercyl, secsize and anything required for a block i/o read 85 * operation in the driver's strategy/start routines 86 * must be filled in before calling us. 87 * 88 * If dos partition table requested, attempt to load it and 89 * find disklabel inside a DOS partition. Also, if bad block 90 * table needed, attempt to extract it as well. Return buffer 91 * for use in signalling errors if requested. 92 * 93 * Returns null on success and an error string on failure. 94 */ 95 const char * 96 readdisklabel(dev_t dev, void (*strat)(struct buf *), struct disklabel *lp, 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 const 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_cflags = BC_BUSY; 144 bp->b_flags |= B_READ; 145 bp->b_cylinder = MBR_BBSECTOR / lp->d_secpercyl; 146 (*strat)(bp); 147 148 /* if successful, wander through dos partition table */ 149 if (biowait(bp)) { 150 msg = "dos partition I/O error"; 151 goto done; 152 } else { 153 struct mbr_partition *ourdp = NULL; 154 155 ourdp = mbr_findslice(dp, bp); 156 if (ourdp == NO_MBR_SIGNATURE) 157 goto nombrpart; 158 159 for (i = 0; i < MBR_PART_COUNT; i++, dp++) { 160 /* Install in partition e, f, g, or h. */ 161 pp = &lp->d_partitions[RAW_PART + 1 + i]; 162 pp->p_offset = dp->mbrp_start; 163 pp->p_size = dp->mbrp_size; 164 if (dp->mbrp_type == MBR_PTYPE_LNXEXT2) 165 pp->p_fstype = FS_EX2FS; 166 167 if (dp->mbrp_type == MBR_PTYPE_LNXSWAP) 168 pp->p_fstype = FS_SWAP; 169 170 /* is this ours? */ 171 if (dp == ourdp) { 172 /* need sector address for SCSI/IDE, 173 cylinder for ESDI/ST506/RLL */ 174 dospartoff = dp->mbrp_start; 175 cyl = MBR_PCYL(dp->mbrp_scyl, dp->mbrp_ssect); 176 177 /* update disklabel with details */ 178 lp->d_partitions[2].p_size = 179 dp->mbrp_size; 180 lp->d_partitions[2].p_offset = 181 dp->mbrp_start; 182 } 183 } 184 lp->d_npartitions = RAW_PART + 1 + i; 185 } 186 187 nombrpart: 188 /* next, dig out disk label */ 189 bp->b_blkno = dospartoff + LABELSECTOR; 190 bp->b_cylinder = cyl; 191 bp->b_bcount = lp->d_secsize; 192 bp->b_cflags = BC_BUSY; 193 bp->b_flags = B_READ; 194 (*strat)(bp); 195 196 /* if successful, locate disk label within block and validate */ 197 if (biowait(bp)) { 198 msg = "disk label I/O error"; 199 goto done; 200 } 201 for (dlp = (struct disklabel *)bp->b_data; 202 dlp <= (struct disklabel *) 203 ((char *)bp->b_data + lp->d_secsize - sizeof(*dlp)); 204 dlp = (struct disklabel *)((char *)dlp + sizeof(long))) { 205 if (dlp->d_magic != DISKMAGIC || dlp->d_magic2 != DISKMAGIC) { 206 if (msg == NULL) 207 msg = "no disk label"; 208 } else if (dlp->d_npartitions > MAXPARTITIONS || 209 dkcksum(dlp) != 0) 210 msg = "disk label corrupted"; 211 else { 212 *lp = *dlp; 213 msg = NULL; 214 break; 215 } 216 } 217 218 if (msg) 219 goto done; 220 221 /* obtain bad sector table if requested and present */ 222 if (osdep && (lp->d_flags & D_BADSECT)) { 223 struct dkbad *db; 224 225 bdp = &osdep->bad; 226 i = 0; 227 do { 228 /* read a bad sector table */ 229 bp->b_cflags = BC_BUSY; 230 bp->b_flags = B_READ; 231 bp->b_blkno = lp->d_secperunit - lp->d_nsectors + i; 232 if (lp->d_secsize > DEV_BSIZE) 233 bp->b_blkno *= lp->d_secsize / DEV_BSIZE; 234 else 235 bp->b_blkno /= DEV_BSIZE / lp->d_secsize; 236 bp->b_bcount = lp->d_secsize; 237 bp->b_cylinder = lp->d_ncylinders - 1; 238 (*strat)(bp); 239 240 /* if successful, validate, otherwise try another */ 241 if (biowait(bp)) { 242 msg = "bad sector table I/O error"; 243 } else { 244 db = (struct dkbad *)(bp->b_data); 245 #define DKBAD_MAGIC 0x4321 246 if (db->bt_mbz == 0 247 && db->bt_flag == DKBAD_MAGIC) { 248 msg = NULL; 249 *bdp = *db; 250 break; 251 } else 252 msg = "bad sector table corrupted"; 253 } 254 } while (bp->b_error && (i += 2) < 10 && 255 i < lp->d_nsectors); 256 } 257 258 done: 259 brelse(bp, BC_INVAL); 260 return (msg); 261 } 262 263 /* 264 * Check new disk label for sensibility 265 * before setting it. 266 */ 267 int 268 setdisklabel(struct disklabel *olp, struct disklabel *nlp, u_long openmask, struct cpu_disklabel *osdep) 269 { 270 int i; 271 struct partition *opp, *npp; 272 273 /* sanity clause */ 274 if (nlp->d_secpercyl == 0 || nlp->d_secsize == 0 275 || (nlp->d_secsize % DEV_BSIZE) != 0) 276 return(EINVAL); 277 278 /* special case to allow disklabel to be invalidated */ 279 if (nlp->d_magic == 0xffffffff) { 280 *olp = *nlp; 281 return (0); 282 } 283 284 if (nlp->d_magic != DISKMAGIC || nlp->d_magic2 != DISKMAGIC || 285 dkcksum(nlp) != 0) 286 return (EINVAL); 287 288 /* XXX missing check if other dos partitions will be overwritten */ 289 290 while (openmask != 0) { 291 i = ffs(openmask) - 1; 292 openmask &= ~(1 << i); 293 if (nlp->d_npartitions <= i) 294 return (EBUSY); 295 opp = &olp->d_partitions[i]; 296 npp = &nlp->d_partitions[i]; 297 if (npp->p_offset != opp->p_offset || npp->p_size < opp->p_size) 298 return (EBUSY); 299 /* 300 * Copy internally-set partition information 301 * if new label doesn't include it. XXX 302 */ 303 if (npp->p_fstype == FS_UNUSED && opp->p_fstype != FS_UNUSED) { 304 npp->p_fstype = opp->p_fstype; 305 npp->p_fsize = opp->p_fsize; 306 npp->p_frag = opp->p_frag; 307 npp->p_cpg = opp->p_cpg; 308 } 309 } 310 nlp->d_checksum = 0; 311 nlp->d_checksum = dkcksum(nlp); 312 *olp = *nlp; 313 return (0); 314 } 315 316 317 /* 318 * Write disk label back to device after modification. 319 */ 320 int 321 writedisklabel(dev_t dev, void (*strat)(struct buf *), struct disklabel *lp, 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_cflags = BC_BUSY; 343 bp->b_flags = B_READ; 344 bp->b_cylinder = MBR_BBSECTOR / lp->d_secpercyl; 345 (*strat)(bp); 346 347 if ((error = biowait(bp)) == 0) { 348 struct mbr_partition *ourdp = NULL; 349 350 ourdp = mbr_findslice(dp, bp); 351 if (ourdp == NO_MBR_SIGNATURE) 352 goto nombrpart; 353 354 if (ourdp) { 355 /* need sector address for SCSI/IDE, 356 cylinder for ESDI/ST506/RLL */ 357 dospartoff = ourdp->mbrp_start; 358 cyl = MBR_PCYL(ourdp->mbrp_scyl, ourdp->mbrp_ssect); 359 } 360 } 361 362 nombrpart: 363 /* next, dig out disk label */ 364 bp->b_blkno = dospartoff + LABELSECTOR; 365 bp->b_cylinder = cyl; 366 bp->b_bcount = lp->d_secsize; 367 bp->b_cflags = BC_BUSY; 368 bp->b_flags = B_READ; 369 (*strat)(bp); 370 371 /* if successful, locate disk label within block and validate */ 372 if ((error = biowait(bp)) != 0) 373 goto done; 374 for (dlp = (struct disklabel *)bp->b_data; 375 dlp <= (struct disklabel *) 376 ((char *)bp->b_data + lp->d_secsize - sizeof(*dlp)); 377 dlp = (struct disklabel *)((char *)dlp + sizeof(long))) { 378 if (dlp->d_magic == DISKMAGIC && dlp->d_magic2 == DISKMAGIC && 379 dkcksum(dlp) == 0) { 380 *dlp = *lp; 381 bp->b_cflags = BC_BUSY; 382 bp->b_flags = B_WRITE; 383 (*strat)(bp); 384 error = biowait(bp); 385 goto done; 386 } 387 } 388 error = ESRCH; 389 390 done: 391 brelse(bp, BC_INVAL); 392 return (error); 393 } 394