1 /* $NetBSD: disksubr.c,v 1.23 2026/07/14 13:34:35 thorpej 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.23 2026/07/14 13:34:35 thorpej 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 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 partition. 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 *)((char*)bp->b_data + MBR_MAGIC_OFFSET); 61 if (*mbrmagicp != MBR_MAGIC) 62 return (NO_MBR_SIGNATURE); 63 64 /* XXX how do we check veracity/bounds of this? */ 65 memcpy(dp, (char*)bp->b_data + MBR_PART_OFFSET, MBR_PART_COUNT * sizeof(*dp)); 66 67 /* look for NetBSD partition */ 68 for (i = 0; i < MBR_PART_COUNT; i++) { 69 if (dp[i].mbrp_type == 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 const 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 buf *bp; 101 struct disklabel *dlp; 102 const char *msg = NULL; 103 int dospartoff, cyl, i; 104 105 /* minimal requirements for archetypal disk label */ 106 if (lp->d_secsize == 0) 107 lp->d_secsize = DEV_BSIZE; 108 if (lp->d_secperunit == 0) 109 lp->d_secperunit = 0x1fffffff; 110 #if 0 111 if (lp->d_ncylinders == 16383) { 112 printf("disklabel: Disk > 8G ... readjusting chs %d/%d/%d to ", 113 lp->d_ncylinders, lp->d_ntracks, lp->d_nsectors); 114 lp->d_ncylinders = lp->d_secperunit / lp->d_ntracks / lp->d_nsectors; 115 printf("%d/%d/%d\n", 116 lp->d_ncylinders, lp->d_ntracks, lp->d_nsectors); 117 } 118 #endif 119 lp->d_npartitions = RAW_PART + 1; 120 for (i = 0; i < RAW_PART; i++) { 121 lp->d_partitions[i].p_size = 0; 122 lp->d_partitions[i].p_offset = 0; 123 } 124 if (lp->d_partitions[i].p_size == 0) 125 lp->d_partitions[i].p_size = 0x1fffffff; 126 lp->d_partitions[i].p_offset = 0; 127 128 /* get a buffer and initialize it */ 129 bp = geteblk((int)lp->d_secsize); 130 bp->b_dev = dev; 131 132 /* do dos partitions in the process of getting disklabel? */ 133 dospartoff = 0; 134 cyl = LABELSECTOR / lp->d_secpercyl; 135 if (!osdep) 136 goto nombrpart; 137 dp = osdep->dosparts; 138 139 /* read master boot record */ 140 bp->b_blkno = MBR_BBSECTOR; 141 bp->b_bcount = lp->d_secsize; 142 bp->b_flags |= B_READ; 143 bp->b_cylinder = MBR_BBSECTOR / lp->d_secpercyl; 144 (*strat)(bp); 145 146 /* if successful, wander through dos partition table */ 147 if (biowait(bp)) { 148 msg = "dos partition I/O error"; 149 goto done; 150 } else { 151 struct mbr_partition *ourdp = NULL; 152 153 ourdp = mbr_findslice(dp, bp); 154 if (ourdp == NO_MBR_SIGNATURE) 155 goto nombrpart; 156 157 for (i = 0; i < MBR_PART_COUNT; i++, dp++) { 158 /* Install in partition e, f, g, or h. */ 159 pp = &lp->d_partitions[RAW_PART + 1 + i]; 160 pp->p_offset = dp->mbrp_start; 161 pp->p_size = dp->mbrp_size; 162 if (dp->mbrp_type == MBR_PTYPE_LNXEXT2) 163 pp->p_fstype = FS_EX2FS; 164 165 if (dp->mbrp_type == MBR_PTYPE_LNXSWAP) 166 pp->p_fstype = FS_SWAP; 167 168 /* is this ours? */ 169 if (dp == ourdp) { 170 /* need sector address for SCSI/IDE, 171 cylinder for ESDI/ST506/RLL */ 172 dospartoff = dp->mbrp_start; 173 cyl = MBR_PCYL(dp->mbrp_scyl, dp->mbrp_ssect); 174 175 /* update disklabel with details */ 176 lp->d_partitions[2].p_size = 177 dp->mbrp_size; 178 lp->d_partitions[2].p_offset = 179 dp->mbrp_start; 180 } 181 } 182 lp->d_npartitions = RAW_PART + 1 + i; 183 } 184 185 nombrpart: 186 /* next, dig out disk label */ 187 bp->b_blkno = dospartoff + LABELSECTOR; 188 bp->b_cylinder = cyl; 189 bp->b_bcount = lp->d_secsize; 190 bp->b_oflags &= ~(BO_DONE); 191 bp->b_flags |= B_READ; 192 (*strat)(bp); 193 194 /* if successful, locate disk label within block and validate */ 195 if (biowait(bp)) { 196 msg = "disk label I/O error"; 197 goto done; 198 } 199 for (dlp = (struct disklabel *)bp->b_data; 200 dlp <= (struct disklabel *)((char*)bp->b_data + lp->d_secsize - sizeof(*dlp)); 201 dlp = (struct disklabel *)((char *)dlp + sizeof(long))) { 202 if (dlp->d_magic != DISKMAGIC || dlp->d_magic2 != DISKMAGIC) { 203 if (msg == NULL) 204 msg = "no disk label"; 205 } else if (dlp->d_npartitions > MAXPARTITIONS || 206 dkcksum(dlp) != 0) 207 msg = "disk label corrupted"; 208 else { 209 *lp = *dlp; 210 msg = NULL; 211 break; 212 } 213 } 214 215 done: 216 brelse(bp, 0); 217 return (msg); 218 } 219 220 /* 221 * Write disk label back to device after modification. 222 */ 223 int 224 writedisklabel(dev_t dev, void (*strat)(struct buf *), struct disklabel *lp, 225 struct cpu_disklabel *osdep) 226 { 227 struct mbr_partition *dp; 228 struct buf *bp; 229 struct disklabel *dlp; 230 int error, dospartoff, cyl; 231 232 /* get a buffer and initialize it */ 233 bp = geteblk((int)lp->d_secsize); 234 bp->b_dev = dev; 235 236 /* do dos partitions in the process of getting disklabel? */ 237 dospartoff = 0; 238 cyl = LABELSECTOR / lp->d_secpercyl; 239 if (!osdep) 240 goto nombrpart; 241 dp = osdep->dosparts; 242 243 /* read master boot record */ 244 bp->b_blkno = MBR_BBSECTOR; 245 bp->b_bcount = lp->d_secsize; 246 bp->b_flags |= B_READ; 247 bp->b_cylinder = MBR_BBSECTOR / lp->d_secpercyl; 248 (*strat)(bp); 249 250 if ((error = biowait(bp)) == 0) { 251 struct mbr_partition *ourdp = NULL; 252 253 ourdp = mbr_findslice(dp, bp); 254 if (ourdp == NO_MBR_SIGNATURE) 255 goto nombrpart; 256 257 if (ourdp) { 258 /* need sector address for SCSI/IDE, 259 cylinder for ESDI/ST506/RLL */ 260 dospartoff = ourdp->mbrp_start; 261 cyl = MBR_PCYL(ourdp->mbrp_scyl, ourdp->mbrp_ssect); 262 } 263 } 264 265 nombrpart: 266 /* next, dig out disk label */ 267 bp->b_blkno = dospartoff + LABELSECTOR; 268 bp->b_cylinder = cyl; 269 bp->b_bcount = lp->d_secsize; 270 bp->b_oflags &= ~(BO_DONE); 271 bp->b_flags |= B_READ; 272 (*strat)(bp); 273 274 /* if successful, locate disk label within block and validate */ 275 if ((error = biowait(bp)) != 0) 276 goto done; 277 for (dlp = (struct disklabel *)bp->b_data; 278 dlp <= (struct disklabel *)((char*)bp->b_data + lp->d_secsize - sizeof(*dlp)); 279 dlp = (struct disklabel *)((char *)dlp + sizeof(long))) { 280 if (dlp->d_magic == DISKMAGIC && dlp->d_magic2 == DISKMAGIC && 281 dkcksum(dlp) == 0) { 282 *dlp = *lp; 283 bp->b_oflags &= ~(BO_DONE); 284 bp->b_flags &= ~(B_READ); 285 bp->b_flags |= B_WRITE; 286 (*strat)(bp); 287 error = biowait(bp); 288 goto done; 289 } 290 } 291 error = ESRCH; 292 293 done: 294 brelse(bp, 0); 295 return (error); 296 } 297