xref: /386bsd/usr/src/kernel/kern/subr/disklabel.c (revision a2142627)
1 /*
2  * Copyright (c) 1982, 1986, 1988 Regents of the University of California.
3  * All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
8  * 1. Redistributions of source code must retain the above copyright
9  *    notice, this list of conditions and the following disclaimer.
10  * 2. Redistributions in binary form must reproduce the above copyright
11  *    notice, this list of conditions and the following disclaimer in the
12  *    documentation and/or other materials provided with the distribution.
13  * 3. All advertising materials mentioning features or use of this software
14  *    must display the following acknowledgement:
15  *	This product includes software developed by the University of
16  *	California, Berkeley and its contributors.
17  * 4. Neither the name of the University nor the names of its contributors
18  *    may be used to endorse or promote products derived from this software
19  *    without specific prior written permission.
20  *
21  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
22  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
23  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
24  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
25  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
26  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
27  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
28  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
29  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
30  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
31  * SUCH DAMAGE.
32  *
33  *	$Id: disklabel.c,v 1.1 94/10/19 18:33:17 bill Exp $
34  */
35 
36 #include "sys/param.h"
37 #include "sys/syslog.h"
38 #include "sys/errno.h"
39 #include "buf.h"
40 #include "dkbad.h"
41 #include "disklabel.h"
42 #include "prototypes.h"
43 
44 /*
45  * Seek sort for disks.  We depend on the driver
46  * which calls us using b_resid as the current cylinder number.
47  *
48  * The argument dp structure holds a b_actf activity chain pointer
49  * on which we keep two queues, sorted in ascending cylinder order.
50  * The first queue holds those requests which are positioned after
51  * the current cylinder (in the first request); the second holds
52  * requests which came in after their cylinder number was passed.
53  * Thus we implement a one way scan, retracting after reaching the
54  * end of the drive to the first request on the second queue,
55  * at which time it becomes the first queue.
56  *
57  * A one-way scan is natural because of the way UNIX read-ahead
58  * blocks are allocated.
59  */
60 
61 #define	b_cylin	b_resid
62 
63 /* XXX make part of dkif interface, ala devif ... */
64 #define dkunit(dev)		(minor(dev) >> 3)
65 #define dkpart(dev)		(minor(dev) & 7)
66 #define dkminor(unit, part)	(((unit) << 3) | (part))
67 
68 /*
69  * Attempt to read a disk label from a device
70  * using the indicated stategy routine.
71  * The label must be partly set up before this:
72  * secpercyl, secsize and anything required for a block i/o read
73  * operation in the driver's strategy/start routines
74  * must be filled in before calling us.
75  *
76  * If dos partition table requested, attempt to load it and
77  * find disklabel inside a DOS partition. Also, if bad block
78  * table needed, attempt to extract it as well. Return buffer
79  * for use in signalling errors if requested.
80  *
81  * Returns null on success and an error string on failure.
82  */
83 char *
readdisklabel(dev,strat,lp,dp,bdp,bpp)84 readdisklabel(dev, strat, lp, dp, bdp, bpp)
85 	dev_t dev;
86 	int (*strat)();
87 	register struct disklabel *lp;
88 	struct dos_partition *dp;
89 	struct dkbad *bdp;
90 	struct buf **bpp;
91 {
92 	register struct buf *bp;
93 	struct disklabel *dlp;
94 	char *msg = NULL;
95 	int cyl, dospartoff, i;
96 
97 	/* minimal requirements for archtypal disk label */
98 	if (lp->d_secperunit == 0)
99 		lp->d_secperunit = 0x1fffffff;
100 	lp->d_npartitions = 1;
101 	if (lp->d_partitions[0].p_size == 0)
102 		lp->d_partitions[0].p_size = 0x1fffffff;
103 	lp->d_partitions[0].p_offset = 0;
104 
105 	/* obtain buffer to probe drive with */
106 	bp = geteblk((int)lp->d_secsize);
107 
108 	/* request no partition relocation by driver on I/O operations */
109 	bp->b_dev = makedev(major(dev), dkminor((dkunit(dev)), 3));
110 
111 	/* do dos partitions in the process of getting disklabel? */
112 	dospartoff = 0;
113 	cyl = LABELSECTOR / lp->d_secpercyl;
114 	if (dp) {
115 		struct dos_partition *ap;
116 
117 		/* read master boot record */
118 		bp->b_blkno = DOSBBSECTOR;
119 		bp->b_bcount = lp->d_secsize;
120 		bp->b_flags = B_BUSY | B_READ;
121 		bp->b_cylin = DOSBBSECTOR / lp->d_secpercyl;
122 		(*strat)(bp);
123 
124 		/* if successful, wander through dos partition table */
125 		if (biowait(bp)) {
126 			msg = "dos partition I/O error";
127 			goto done;
128 		} else {
129 
130 			/* XXX how do we check veracity/bounds of this? */
131 			memcpy(dp, bp->b_un.b_addr + DOSPARTOFF,
132 				NDOSPART * sizeof(*dp));
133 			for (i = 0; i < NDOSPART; i++, dp++)
134 
135 				/* is this ours? */
136 				if (dp->dp_size &&
137 					dp->dp_typ == DOSPTYP_386BSD
138 					&& dospartoff == 0) {
139 
140 					/* need sector address for SCSI/IDE,
141 					   cylinder for ESDI/ST506/RLL */
142 					dospartoff = dp->dp_start;
143 					cyl = DPCYL(dp->dp_scyl, dp->dp_ssect);
144 
145 					/* update disklabel with details */
146 					lp->d_partitions[0].p_size =
147 						dp->dp_size;
148 					lp->d_partitions[0].p_offset =
149 						dp->dp_start;
150 					lp->d_ntracks = dp->dp_ehd + 1;
151 					lp->d_nsectors = DPSECT(dp->dp_esect);
152 					lp->d_subtype |= (lp->d_subtype & 3)
153 							+ i | DSTYPE_INDOSPART;
154 					lp->d_secpercyl = lp->d_ntracks *
155 						lp->d_nsectors;
156 				}
157 		}
158 
159 	}
160 
161 	/* next, dig out disk label */
162 	bp->b_blkno = dospartoff + LABELSECTOR;
163 	bp->b_cylin = cyl;
164 	bp->b_bcount = lp->d_secsize;
165 	bp->b_flags = B_BUSY | B_READ;
166 	(*strat)(bp);
167 
168 	/* if successful, locate disk label within block and validate */
169 	if (biowait(bp)) {
170 		msg = "disk label I/O error";
171 		goto done;
172 	} else for (dlp = (struct disklabel *)bp->b_un.b_addr;
173 	    dlp <= (struct disklabel *)(bp->b_un.b_addr+DEV_BSIZE-sizeof(*dlp));
174 	    dlp = (struct disklabel *)((char *)dlp + sizeof(long))) {
175 		if (dlp->d_magic != DISKMAGIC || dlp->d_magic2 != DISKMAGIC) {
176 			if (msg == NULL)
177 				msg = "no disk label";
178 		} else if (dlp->d_npartitions > MAXPARTITIONS ||
179 			   dkcksum(dlp) != 0)
180 			msg = "disk label corrupted";
181 		else {
182 			*lp = *dlp;
183 			msg = NULL;
184 			break;
185 		}
186 	}
187 
188 	if (msg)
189 		goto done;
190 
191 	/* obtain bad sector table if requested and present */
192 	if (bdp && (lp->d_flags & D_BADSECT)) {
193 		struct dkbad *db;
194 
195 		i = 0;
196 		do {
197 			/* read a bad sector table */
198 			bp->b_flags = B_BUSY | B_READ;
199 			bp->b_blkno = lp->d_secperunit - lp->d_nsectors + i;
200 			if (lp->d_secsize > DEV_BSIZE)
201 				bp->b_blkno *= lp->d_secsize / DEV_BSIZE;
202 			else
203 				bp->b_blkno /= DEV_BSIZE / lp->d_secsize;
204 			bp->b_bcount = lp->d_secsize;
205 			bp->b_cylin = lp->d_ncylinders - 1;
206 			(*strat)(bp);
207 
208 			/* if successful, validate, otherwise try another */
209 			if (biowait(bp)) {
210 				msg = "bad sector table I/O error";
211 			} else {
212 				db = (struct dkbad *)(bp->b_un.b_addr);
213 #define DKBAD_MAGIC 0x4321
214 				if (db->bt_mbz == 0
215 					&& db->bt_flag == DKBAD_MAGIC) {
216 					msg = NULL;
217 					*bdp = *db;
218 					break;
219 				} else
220 					msg = "bad sector table corrupted";
221 			}
222 		} while ((bp->b_flags & B_ERROR) && (i += 2) < 10 &&
223 			i < lp->d_nsectors);
224 	}
225 
226 done:
227 	bp->b_flags = B_INVAL | B_AGE | B_READ;
228 #ifndef old
229 	/* if desired, pass back allocated block so caller can use */
230 	if (bpp)
231 		*bpp = bp;
232 	else
233 #endif
234 	brelse(bp);
235 	return (msg);
236 }
237 
238 /*
239  * Check new disk label for sensibility
240  * before setting it.
241  */
setdisklabel(olp,nlp,openmask,dp)242 setdisklabel(olp, nlp, openmask, dp)
243 	register struct disklabel *olp, *nlp;
244 	u_long openmask;
245 	struct dos_partition *dp;
246 {
247 	register i;
248 	register struct partition *opp, *npp;
249 
250 	/* sanity clause */
251 	if (nlp->d_secpercyl == 0 || nlp->d_secsize == 0
252 		|| (nlp->d_secsize % DEV_BSIZE) != 0)
253 			return(EINVAL);
254 
255 	/* special case to allow disklabel to be invalidated */
256 	if (nlp->d_magic == 0xffffffff) {
257 		*olp = *nlp;
258 		return (0);
259 	}
260 
261 	if (nlp->d_magic != DISKMAGIC || nlp->d_magic2 != DISKMAGIC ||
262 	    dkcksum(nlp) != 0)
263 		return (EINVAL);
264 
265 	/* XXX missing check if other dos partitions will be overwritten */
266 
267 	while ((i = ffs((long)openmask)) != 0) {
268 		i--;
269 		openmask &= ~(1 << i);
270 		if (nlp->d_npartitions <= i)
271 			return (EBUSY);
272 		opp = &olp->d_partitions[i];
273 		npp = &nlp->d_partitions[i];
274 		if (npp->p_offset != opp->p_offset || npp->p_size < opp->p_size)
275 			return (EBUSY);
276 		/*
277 		 * Copy internally-set partition information
278 		 * if new label doesn't include it.		XXX
279 		 */
280 		if (npp->p_fstype == FS_UNUSED && opp->p_fstype != FS_UNUSED) {
281 			npp->p_fstype = opp->p_fstype;
282 			npp->p_fsize = opp->p_fsize;
283 			npp->p_frag = opp->p_frag;
284 			npp->p_cpg = opp->p_cpg;
285 		}
286 	}
287  	nlp->d_checksum = 0;
288  	nlp->d_checksum = dkcksum(nlp);
289 	*olp = *nlp;
290 	return (0);
291 }
292 
293 
294 /*
295  * Write disk label back to device after modification.
296  */
writedisklabel(dev,strat,lp,dp)297 writedisklabel(dev, strat, lp, dp)
298 	dev_t dev;
299 	int (*strat)();
300 	register struct disklabel *lp;
301 	struct dos_partition *dp;
302 {
303 	struct buf *bp;
304 	struct disklabel *dlp;
305 	int labelpart, error = 0, dospartoff, cyl, i;
306 
307 	labelpart = dkpart(dev);
308 #ifdef nope
309 	if (lp->d_partitions[labelpart].p_offset != 0) {
310 		if (lp->d_partitions[0].p_offset != 0)
311 			return (EXDEV);			/* not quite right */
312 		labelpart = 0;
313 	}
314 #else
315 		labelpart = 3;
316 #endif
317 
318 	bp = geteblk((int)lp->d_secsize);
319 
320 	/* request no partition relocation by driver on I/O operations */
321 	bp->b_dev = makedev(major(dev), dkminor((dkunit(dev)), 3));
322 
323 	/* do dos partitions in the process of getting disklabel? */
324 	dospartoff = 0;
325 	cyl = LABELSECTOR / lp->d_secpercyl;
326 	if (dp) {
327 		bp->b_blkno = DOSBBSECTOR;
328 		bp->b_bcount = lp->d_secsize;
329 		bp->b_flags = B_BUSY | B_READ;
330 		bp->b_cylin = DOSBBSECTOR / lp->d_secpercyl;
331 		(*strat)(bp);
332 		if ((error = biowait(bp)) == 0) {
333 			memcpy(dp, bp->b_un.b_addr + DOSPARTOFF,
334 				NDOSPART * sizeof(*dp));
335 			for (i = 0; i < NDOSPART; i++, dp++)
336 				if(dp->dp_size && dp->dp_typ == DOSPTYP_386BSD
337 					&& dospartoff == 0) {
338 					/* need sector address for SCSI/IDE,
339 					   cylinder for ESDI/ST506/RLL */
340 					dospartoff = dp->dp_start;
341 					cyl = dp->dp_scyl |
342 						((dp->dp_ssect & 0xc0) << 2);
343 				}
344 		}
345 
346 	}
347 
348 #ifdef maybe
349 	/* disklabel in appropriate location? */
350 	if (lp->d_partitions[0].p_offset != 0
351 		&& lp->d_partitions[0].p_offset != dospartoff) {
352 		error = EXDEV;
353 		goto done;
354 	}
355 #endif
356 
357 	bp->b_blkno = dospartoff + LABELSECTOR;
358 	bp->b_cylin = cyl;
359 	bp->b_bcount = lp->d_secsize;
360 	bp->b_flags = B_READ;
361 	(*strat)(bp);
362 	if (error = biowait(bp))
363 		goto done;
364 	for (dlp = (struct disklabel *)bp->b_un.b_addr;
365 	    dlp <= (struct disklabel *)
366 	      (bp->b_un.b_addr + lp->d_secsize - sizeof(*dlp));
367 	    dlp = (struct disklabel *)((char *)dlp + sizeof(long))) {
368 		if (dlp->d_magic == DISKMAGIC && dlp->d_magic2 == DISKMAGIC &&
369 		    (dkcksum(dlp) == 0)) {
370 			*dlp = *lp;
371 			bp->b_flags = B_WRITE;
372 			(*strat)(bp);
373 			error = biowait(bp);
374 			goto done;
375 		}
376 	}
377 	error = ESRCH;
378 done:
379 	brelse(bp);
380 	return (error);
381 }
382 
383 /*
384  * Compute checksum for disk label.
385  */
dkcksum(lp)386 dkcksum(lp)
387 	register struct disklabel *lp;
388 {
389 	register u_short *start, *end;
390 	register u_short sum = 0;
391 
392 	start = (u_short *)lp;
393 	end = (u_short *)&lp->d_partitions[lp->d_npartitions];
394 	while (start < end)
395 		sum ^= *start++;
396 	return (sum);
397 }
398 
399 /*
400  * Determine the size of the transfer, and make sure it is
401  * within the boundaries of the partition. Adjust transfer
402  * if needed, and signal errors or early completion.
403  */
404 int
bounds_check_with_label(struct buf * bp,struct disklabel * lp,int wlabel)405 bounds_check_with_label(struct buf *bp, struct disklabel *lp, int wlabel)
406 {
407 	struct partition *p = lp->d_partitions + dkpart(bp->b_dev);
408 	int labelsect = lp->d_partitions[0].p_offset;
409 	int maxsz = p->p_size,
410 		sz = (bp->b_bcount + DEV_BSIZE - 1) >> DEV_BSHIFT;
411 
412 	/* overwriting disk label ? */
413 	/* XXX should also protect bootstrap in first 8K */
414         if (bp->b_blkno + p->p_offset <= LABELSECTOR + labelsect &&
415 #if LABELSECTOR != 0
416             bp->b_blkno + p->p_offset + sz > LABELSECTOR + labelsect &&
417 #endif
418             (bp->b_flags & B_READ) == 0 && wlabel == 0) {
419                 bp->b_error = EROFS;
420                 goto bad;
421         }
422 
423 #if	defined(DOSBBSECTOR) && defined(notyet)
424 	/* overwriting master boot record? */
425         if (bp->b_blkno + p->p_offset <= DOSBBSECTOR &&
426             (bp->b_flags & B_READ) == 0 && wlabel == 0) {
427                 bp->b_error = EROFS;
428                 goto bad;
429         }
430 #endif
431 
432 	/* beyond partition? */
433         if (bp->b_blkno < 0 || bp->b_blkno + sz > maxsz) {
434 
435                 /* if exactly at end of disk, return an EOF */
436                 if (bp->b_blkno == maxsz) {
437                         bp->b_resid = bp->b_bcount;
438                         return(0);
439                 }
440 
441                 /* or truncate if part of it fits */
442                 sz = maxsz - bp->b_blkno;
443                 if (sz <= 0) {
444 			bp->b_error = EINVAL;
445                         goto bad;
446 		}
447                 bp->b_bcount = sz << DEV_BSHIFT;
448         }
449 
450 	/* calculate cylinder for disksort to order transfers with */
451         bp->b_cylin = (bp->b_blkno + p->p_offset) / lp->d_secpercyl;
452 	return(1);
453 
454 bad:
455 	bp->b_flags |= B_ERROR;
456 	return(-1);
457 }
458 
459 /*
460  * Disk error is the preface to plaintive error messages
461  * about failing disk transfers.  It prints messages of the form
462 
463 hp0g: hard error reading fsbn 12345 of 12344-12347 (hp0 bn %d cn %d tn %d sn %d)
464 
465  * if the offset of the error in the transfer and a disk label
466  * are both available.  blkdone should be -1 if the position of the error
467  * is unknown; the disklabel pointer may be null from drivers that have not
468  * been converted to use them.  The message is printed with printf
469  * if pri is LOG_PRINTF, otherwise it uses log at the specified priority.
470  * The message should be completed (with at least a newline) with printf
471  * or addlog, respectively.  There is no trailing space.
472  */
473 void
diskerr(bp,dname,what,pri,blkdone,lp)474 diskerr(bp, dname, what, pri, blkdone, lp)
475 	register struct buf *bp;
476 	char *dname, *what;
477 	int pri, blkdone;
478 	register struct disklabel *lp;
479 {
480 	int unit = dkunit(bp->b_dev), part = dkpart(bp->b_dev);
481 	register void (*pr) __P((const char *, ...));
482 	char partname = 'a' + part;
483 	int sn;
484 
485 	if (pri != LOG_PRINTF) {
486 		log(pri, "");
487 		pr = addlog;
488 	} else
489 		pr = printf;
490 	(*pr)("%s%d%c: %s %sing fsbn ", dname, unit, partname, what,
491 	    bp->b_flags & B_READ ? "read" : "writ");
492 	sn = bp->b_blkno;
493 	if (bp->b_bcount <= DEV_BSIZE)
494 		(*pr)("%d", sn);
495 	else {
496 		if (blkdone >= 0) {
497 			sn += blkdone;
498 			(*pr)("%d of ", sn);
499 		}
500 		(*pr)("%d-%d", bp->b_blkno,
501 		    bp->b_blkno + (bp->b_bcount - 1) / DEV_BSIZE);
502 	}
503 	if (lp && (blkdone >= 0 || bp->b_bcount <= lp->d_secsize)) {
504 		sn += lp->d_partitions[part].p_offset;
505 		(*pr)(" (%s%d bn %d; cn %d", dname, unit, sn,
506 		    sn / lp->d_secpercyl);
507 		sn %= lp->d_secpercyl;
508 		(*pr)(" tn %d sn %d)", sn / lp->d_nsectors, sn % lp->d_nsectors);
509 	}
510 }
511