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