1 /* $NetBSD: disksubr.c,v 1.10 2001/08/26 02:47:41 matt Exp $ */ 2 3 /* 4 * Copyright (C) 1996 Wolfgang Solfrank. 5 * Copyright (C) 1996 TooLs GmbH. 6 * All rights reserved. 7 * 8 * Redistribution and use in source and binary forms, with or without 9 * modification, are permitted provided that the following conditions 10 * are met: 11 * 1. Redistributions of source code must retain the above copyright 12 * notice, this list of conditions and the following disclaimer. 13 * 2. Redistributions in binary form must reproduce the above copyright 14 * notice, this list of conditions and the following disclaimer in the 15 * documentation and/or other materials provided with the distribution. 16 * 3. All advertising materials mentioning features or use of this software 17 * must display the following acknowledgement: 18 * This product includes software developed by TooLs GmbH. 19 * 4. The name of TooLs GmbH may not be used to endorse or promote products 20 * derived from this software without specific prior written permission. 21 * 22 * THIS SOFTWARE IS PROVIDED BY TOOLS GMBH ``AS IS'' AND ANY EXPRESS OR 23 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 24 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 25 * IN NO EVENT SHALL TOOLS GMBH BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 26 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, 27 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; 28 * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, 29 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR 30 * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF 31 * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 32 */ 33 #include <sys/param.h> 34 #include <sys/buf.h> 35 #include <sys/conf.h> 36 #include <sys/device.h> 37 #include <sys/disk.h> 38 #include <sys/disklabel.h> 39 #include <sys/disklabel_mbr.h> 40 #include <sys/fcntl.h> 41 #include <sys/ioctl.h> 42 #include <sys/malloc.h> 43 #include <sys/stat.h> 44 #include <sys/systm.h> 45 46 #include "opt_mbr.h" 47 48 static inline unsigned short get_short __P((void *p)); 49 static inline unsigned long get_long __P((void *p)); 50 static int get_netbsd_label __P((dev_t dev, void (*strat)(struct buf *), 51 struct disklabel *lp, daddr_t bno)); 52 static int mbr_to_label __P((dev_t dev, void (*strat)(struct buf *), 53 daddr_t bno, struct disklabel *lp, 54 unsigned short *pnpart, 55 struct cpu_disklabel *osdep, daddr_t off)); 56 57 /* 58 * Little endian access routines 59 */ 60 static inline unsigned short 61 get_short(p) 62 void *p; 63 { 64 unsigned char *cp = p; 65 66 return cp[0] | (cp[1] << 8); 67 } 68 69 static inline unsigned long 70 get_long(p) 71 void *p; 72 { 73 unsigned char *cp = p; 74 75 return cp[0] | (cp[1] << 8) | (cp[2] << 16) | (cp[3] << 24); 76 } 77 78 /* 79 * Get real NetBSD disk label 80 */ 81 static int 82 get_netbsd_label(dev_t dev, void (*strat)(struct buf *), struct disklabel *lp, 83 daddr_t bno) 84 { 85 struct buf *bp; 86 struct disklabel *dlp; 87 88 /* get a buffer and initialize it */ 89 bp = geteblk((int)lp->d_secsize); 90 bp->b_dev = dev; 91 92 /* Now get the label block */ 93 bp->b_blkno = bno + LABELSECTOR; 94 bp->b_bcount = lp->d_secsize; 95 bp->b_flags |= B_READ; 96 bp->b_cylinder = bp->b_blkno / (lp->d_secsize / DEV_BSIZE) / lp->d_secpercyl; 97 (*strat)(bp); 98 99 if (biowait(bp)) 100 goto done; 101 102 for (dlp = (struct disklabel *)bp->b_data; 103 dlp <= (struct disklabel *)(bp->b_data + lp->d_secsize - sizeof (*dlp)); 104 dlp = (struct disklabel *)((char *)dlp + sizeof(long))) { 105 if (dlp->d_magic == DISKMAGIC 106 && dlp->d_magic2 == DISKMAGIC 107 && dlp->d_npartitions <= MAXPARTITIONS 108 && dkcksum(dlp) == 0) { 109 *lp = *dlp; 110 brelse(bp); 111 return 1; 112 } 113 } 114 done: 115 brelse(bp); 116 return 0; 117 } 118 119 /* 120 * Construct disklabel entries from partition entries. 121 */ 122 static int 123 mbr_to_label(dev_t dev, void (*strat)(struct buf *), daddr_t bno, 124 struct disklabel *lp, unsigned short *pnpart, 125 struct cpu_disklabel *osdep, daddr_t off) 126 { 127 static int recursion = 0; 128 struct mbr_partition *mp; 129 struct partition *pp; 130 struct buf *bp; 131 int i, found = 0; 132 133 /* Check for recursion overflow. */ 134 if (recursion > MAXPARTITIONS) 135 return 0; 136 137 /* 138 * Extended partitions seem to be relative to their first occurence? 139 */ 140 if (recursion++ == 1) 141 off = bno; 142 143 /* get a buffer and initialize it */ 144 bp = geteblk((int)lp->d_secsize); 145 bp->b_dev = dev; 146 147 /* Now get the MBR */ 148 bp->b_blkno = bno; 149 bp->b_bcount = lp->d_secsize; 150 bp->b_flags |= B_READ; 151 bp->b_cylinder = bp->b_blkno / (lp->d_secsize / DEV_BSIZE) / lp->d_secpercyl; 152 (*strat)(bp); 153 154 if (biowait(bp)) 155 goto done; 156 157 if (get_short(bp->b_data + MBR_MAGICOFF) != MBR_MAGIC) 158 goto done; 159 160 /* Extract info from MBR partition table */ 161 mp = (struct mbr_partition *)(bp->b_data + MBR_PARTOFF); 162 for (i = 0; i < NMBRPART; i++, mp++) { 163 if (get_long(&mp->mbrp_size)) { 164 switch (mp->mbrp_typ) { 165 case MBR_PTYPE_EXT: 166 if (*pnpart < MAXPARTITIONS) { 167 pp = lp->d_partitions + *pnpart; 168 memset(pp, 0, sizeof *pp); 169 pp->p_size = get_long(&mp->mbrp_size); 170 pp->p_offset = off + get_long(&mp->mbrp_start); 171 ++*pnpart; 172 } 173 found = mbr_to_label(dev, strat, 174 off + get_long(&mp->mbrp_start), 175 lp, pnpart, osdep, off); 176 if (found) 177 goto done; 178 break; 179 #ifdef COMPAT_386BSD_MBRPART 180 case MBR_PTYPE_386BSD: 181 printf("WARNING: old BSD partition ID!\n"); 182 /* FALLTHROUGH */ 183 #endif 184 case MBR_PTYPE_NETBSD: 185 /* Found the real NetBSD partition, use it */ 186 osdep->cd_start = off + get_long(&mp->mbrp_start); 187 found = get_netbsd_label(dev, strat, lp, 188 osdep->cd_start); 189 if (found) 190 goto done; 191 /* FALLTHROUGH */ 192 default: 193 if (*pnpart < MAXPARTITIONS) { 194 pp = lp->d_partitions + *pnpart; 195 memset(pp, 0, sizeof *pp); 196 pp->p_size = get_long(&mp->mbrp_size); 197 pp->p_offset = off + get_long(&mp->mbrp_start); 198 ++*pnpart; 199 } 200 break; 201 } 202 } 203 } 204 done: 205 recursion--; 206 brelse(bp); 207 return found; 208 } 209 210 /* 211 * Attempt to read a disk label from a device 212 * using the indicated strategy routine. 213 * 214 * If we can't find a NetBSD label, we attempt to fake one 215 * based on the MBR (and extended partition) information 216 */ 217 char * 218 readdisklabel(dev_t dev, void (*strat)(struct buf *), struct disklabel *lp, 219 struct cpu_disklabel *osdep) 220 { 221 int i; 222 223 /* Initialize disk label with some defaults */ 224 if (lp->d_secsize == 0) 225 lp->d_secsize = DEV_BSIZE; 226 if (lp->d_secpercyl == 0) 227 lp->d_secpercyl = 1; 228 if (lp->d_secperunit == 0) 229 lp->d_secperunit = 0x7fffffff; 230 lp->d_npartitions = RAW_PART + 1; 231 for (i = 0; i < MAXPARTITIONS; i++) { 232 if (i != RAW_PART) { 233 lp->d_partitions[i].p_size = 0; 234 lp->d_partitions[i].p_offset = 0; 235 } 236 } 237 if (lp->d_partitions[RAW_PART].p_size == 0) { 238 lp->d_partitions[RAW_PART].p_size = lp->d_secperunit; 239 lp->d_partitions[RAW_PART].p_offset = 0; 240 } 241 242 osdep->cd_start = -1; 243 244 mbr_to_label(dev, strat, MBR_BBSECTOR, lp, &lp->d_npartitions, osdep, 0); 245 return 0; 246 } 247 248 /* 249 * Check new disk label for sensibility before setting it. 250 */ 251 int 252 setdisklabel(olp, nlp, openmask, osdep) 253 struct disklabel *olp, *nlp; 254 u_long openmask; 255 struct cpu_disklabel *osdep; 256 { 257 /* sanity clause */ 258 if (nlp->d_secpercyl == 0 || nlp->d_secsize == 0 259 || (nlp->d_secsize % DEV_BSIZE) != 0) 260 return EINVAL; 261 262 /* special case to allow disklabel to be invalidated */ 263 if (nlp->d_magic == 0xffffffff) { 264 *olp = *nlp; 265 return 0; 266 } 267 268 if (nlp->d_magic != DISKMAGIC || nlp->d_magic2 != DISKMAGIC 269 || dkcksum(nlp) != 0) 270 return EINVAL; 271 272 /* openmask parameter ignored */ 273 274 *olp = *nlp; 275 return 0; 276 } 277 278 /* 279 * Write disk label back to device after modification. 280 */ 281 int 282 writedisklabel(dev_t dev, void (*strat)(struct buf *), struct disklabel *lp, 283 struct cpu_disklabel *osdep) 284 { 285 struct buf *bp; 286 int error; 287 struct disklabel label; 288 289 /* 290 * Try to re-read a disklabel, in case he changed the MBR. 291 */ 292 label = *lp; 293 readdisklabel(dev, strat, &label, osdep); 294 if (osdep->cd_start < 0) 295 return EINVAL; 296 297 /* get a buffer and initialize it */ 298 bp = geteblk(lp->d_secsize); 299 bp->b_dev = dev; 300 301 bp->b_blkno = osdep->cd_start + LABELSECTOR; 302 bp->b_cylinder = bp->b_blkno / (lp->d_secsize / DEV_BSIZE) / lp->d_secpercyl; 303 bp->b_bcount = lp->d_secsize; 304 bp->b_flags |= B_WRITE; 305 306 memcpy((caddr_t)bp->b_data, (caddr_t)lp, sizeof *lp); 307 308 (*strat)(bp); 309 error = biowait(bp); 310 311 brelse(bp); 312 313 return error; 314 } 315 316 /* 317 * Determine the size of the transfer, and make sure it is 318 * within the boundaris of the partition. Adjust transfer 319 * if needed, and signal errors or early completion. 320 */ 321 int 322 bounds_check_with_label(bp, lp, wlabel) 323 struct buf *bp; 324 struct disklabel *lp; 325 int wlabel; 326 { 327 struct partition *p = lp->d_partitions + DISKPART(bp->b_dev); 328 int sz; 329 330 sz = howmany(bp->b_bcount, lp->d_secsize); 331 332 if (bp->b_blkno + sz > p->p_size) { 333 sz = p->p_size - bp->b_blkno; 334 if (sz == 0) { 335 /* If axactly at end of disk, return EOF. */ 336 bp->b_resid = bp->b_bcount; 337 goto done; 338 } 339 if (sz < 0) { 340 /* If past end of disk, return EINVAL. */ 341 bp->b_error = EINVAL; 342 goto bad; 343 } 344 /* Otherwise truncate request. */ 345 bp->b_bcount = sz * lp->d_secsize; 346 } 347 348 /* calculate cylinder for disksort to order transfers with */ 349 bp->b_cylinder = (bp->b_blkno + p->p_offset) 350 / (lp->d_secsize / DEV_BSIZE) / lp->d_secpercyl; 351 352 return 1; 353 354 bad: 355 bp->b_flags |= B_ERROR; 356 done: 357 return 0; 358 } 359