disksubr.c revision 1.1 1 /* $NetBSD: disksubr.c,v 1.1 2017/07/24 08:56:29 mrg 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.1 2017/07/24 08:56:29 mrg 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->mbrparts;
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->mbrparts;
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