xref: /openbsd-src/sbin/disklabel/disklabel.c (revision 8445c53715e7030056b779e8ab40efb7820981f2)
1 /*	$OpenBSD: disklabel.c,v 1.71 2001/09/03 16:14:27 millert Exp $	*/
2 
3 /*
4  * Copyright (c) 1987, 1993
5  *	The Regents of the University of California.  All rights reserved.
6  *
7  * This code is derived from software contributed to Berkeley by
8  * Symmetric Computer Systems.
9  *
10  * Redistribution and use in source and binary forms, with or without
11  * modification, are permitted provided that the following conditions
12  * are met:
13  * 1. Redistributions of source code must retain the above copyright
14  *    notice, this list of conditions and the following disclaimer.
15  * 2. Redistributions in binary form must reproduce the above copyright
16  *    notice, this list of conditions and the following disclaimer in the
17  *    documentation and/or other materials provided with the distribution.
18  * 3. All advertising materials mentioning features or use of this software
19  *    must display the following acknowledgement:
20  *	This product includes software developed by the University of
21  *	California, Berkeley and its contributors.
22  * 4. Neither the name of the University nor the names of its contributors
23  *    may be used to endorse or promote products derived from this software
24  *    without specific prior written permission.
25  *
26  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
27  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
28  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
29  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
30  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
31  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
32  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
33  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
34  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
35  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
36  * SUCH DAMAGE.
37  */
38 
39 #ifndef lint
40 static const char copyright[] =
41 "@(#) Copyright (c) 1987, 1993\n\
42 	The Regents of the University of California.  All rights reserved.\n";
43 #endif /* not lint */
44 
45 #ifndef lint
46 static const char rcsid[] = "$OpenBSD: disklabel.c,v 1.71 2001/09/03 16:14:27 millert Exp $";
47 #endif /* not lint */
48 
49 #include <sys/param.h>
50 #include <sys/ioctl.h>
51 #include <sys/stat.h>
52 #include <sys/wait.h>
53 #define DKTYPENAMES
54 #include <sys/disklabel.h>
55 
56 #include <ufs/ffs/fs.h>
57 
58 #include <ctype.h>
59 #include <err.h>
60 #include <errno.h>
61 #include <fcntl.h>
62 #include <signal.h>
63 #include <string.h>
64 #include <stdio.h>
65 #include <stdlib.h>
66 #include <unistd.h>
67 #include <util.h>
68 #include "pathnames.h"
69 
70 /*
71  * Disklabel: read and write disklabels.
72  * The label is usually placed on one of the first sectors of the disk.
73  * Many machines also place a bootstrap in the same area,
74  * in which case the label is embedded in the bootstrap.
75  * The bootstrap source must leave space at the proper offset
76  * for the label on such machines.
77  */
78 
79 #ifndef BBSIZE
80 #define	BBSIZE	8192			/* size of boot area, with label */
81 #endif
82 
83 #ifndef NUMBOOT
84 #define NUMBOOT 0
85 #endif
86 
87 char	*dkname, *specname;
88 char	tmpfil[] = _PATH_TMPFILE;
89 char	namebuf[BBSIZE], *np = namebuf;
90 struct	disklabel lab;
91 char	bootarea[BBSIZE];
92 
93 #if NUMBOOT > 0
94 int	installboot;	/* non-zero if we should install a boot program */
95 char	*bootbuf;	/* pointer to buffer with remainder of boot prog */
96 int	bootsize;	/* size of remaining boot program */
97 char	*xxboot;	/* primary boot */
98 char	*bootxx;	/* secondary boot */
99 char	boot0[MAXPATHLEN];
100 #if NUMBOOT > 1
101 char	boot1[MAXPATHLEN];
102 #endif
103 #endif
104 
105 enum {
106 	UNSPEC, EDIT, EDITOR, READ, RESTORE, SETWRITEABLE, WRITE, WRITEBOOT
107 } op = UNSPEC;
108 
109 int	cflag;
110 int	dflag;
111 int	rflag;
112 int	tflag;
113 int	nwflag;
114 int	verbose;
115 int	donothing;
116 
117 #ifdef DOSLABEL
118 struct dos_partition *dosdp;	/* DOS partition, if found */
119 struct dos_partition *readmbr __P((int));
120 #endif
121 
122 void	makedisktab __P((FILE *, struct disklabel *));
123 void	makelabel __P((char *, char *, struct disklabel *));
124 int	writelabel __P((int, char *, struct disklabel *));
125 void	l_perror __P((char *));
126 struct disklabel *readlabel __P((int));
127 struct disklabel *makebootarea __P((char *, struct disklabel *, int));
128 void	display __P((FILE *, struct disklabel *));
129 void	display_partition __P((FILE *, struct disklabel *, char **, int, char, int));
130 int	width_partition __P((struct disklabel *, int));
131 int	editor __P((struct disklabel *, int, char *, char *));
132 int	edit __P((struct disklabel *, int));
133 int	editit __P((void));
134 char	*skip __P((char *));
135 char	*word __P((char *));
136 int	getasciilabel __P((FILE *, struct disklabel *));
137 int	checklabel __P((struct disklabel *));
138 int	cmplabel __P((struct disklabel *, struct disklabel *));
139 void	setbootflag __P((struct disklabel *));
140 void	usage __P((void));
141 u_short	dkcksum __P((struct disklabel *));
142 
143 int
144 main(argc, argv)
145 	int argc;
146 	char *argv[];
147 {
148 	int ch, f, writeable, error = 0;
149 	char *fstabfile = NULL;
150 	struct disklabel *lp;
151 	FILE *t;
152 
153 	while ((ch = getopt(argc, argv, "BEFf:NRWb:cdenrs:tvw")) != -1)
154 		switch (ch) {
155 #if NUMBOOT > 0
156 		case 'B':
157 			++installboot;
158 			break;
159 		case 'b':
160 			xxboot = optarg;
161 			break;
162 #if NUMBOOT > 1
163 		case 's':
164 			bootxx = optarg;
165 			break;
166 #endif
167 #endif
168 		case 'N':
169 			if (op != UNSPEC)
170 				usage();
171 			writeable = 0;
172 			op = SETWRITEABLE;
173 			break;
174 		case 'R':
175 			if (op != UNSPEC)
176 				usage();
177 			op = RESTORE;
178 			break;
179 		case 'W':
180 			if (op != UNSPEC)
181 				usage();
182 			writeable = 1;
183 			op = SETWRITEABLE;
184 			break;
185 		case 'c':
186 			++cflag;
187 			break;
188 		case 'd':
189 			++dflag;
190 			break;
191 		case 'e':
192 			if (op != UNSPEC)
193 				usage();
194 			op = EDIT;
195 			break;
196 		case 'E':
197 			if (op != UNSPEC)
198 				usage();
199 			op = EDITOR;
200 			break;
201 		case 'f':
202 			fstabfile = optarg;
203 			break;
204 		case 'r':
205 			++rflag;
206 			break;
207 		case 't':
208 			++tflag;
209 			break;
210 		case 'w':
211 			if (op != UNSPEC)
212 				usage();
213 			op = WRITE;
214 			break;
215 		case 'n':
216 			donothing++;
217 			break;
218 		case 'v':
219 			verbose++;
220 			break;
221 		case '?':
222 		default:
223 			usage();
224 	}
225 	argc -= optind;
226 	argv += optind;
227 
228 #if NUMBOOT > 0
229 	if (installboot) {
230 		rflag++;
231 		if (op == UNSPEC)
232 			op = WRITEBOOT;
233 	} else {
234 		if (op == UNSPEC)
235 			op = READ;
236 	}
237 #else
238 	if (op == UNSPEC)
239 		op = READ;
240 #endif
241 
242 	if (argc < 1 || (rflag && cflag + dflag > 0) ||
243 	    (fstabfile && op != EDITOR))
244 		usage();
245 
246 	dkname = argv[0];
247 	f = opendev(dkname, (op == READ ? O_RDONLY : O_RDWR), OPENDEV_PART,
248 	    &specname);
249 	if (f < 0)
250 		err(4, "%s", specname);
251 
252 #ifdef DOSLABEL
253 	/*
254 	 * Check for presence of DOS partition table in
255 	 * master boot record. Return pointer to OpenBSD
256 	 * partition, if present. If no valid partition table,
257 	 * return 0. If valid partition table present, but no
258 	 * partition to use, return a pointer to a non-386bsd
259 	 * partition.
260 	 */
261 	dosdp = readmbr(f);
262 #endif
263 
264 	switch (op) {
265 	case EDIT:
266 		if (argc != 1)
267 			usage();
268 		if ((lp = readlabel(f)) == NULL)
269 			exit(1);
270 		error = edit(lp, f);
271 		break;
272 	case EDITOR:
273 		if (argc != 1)
274 			usage();
275 		if ((lp = readlabel(f)) == NULL)
276 			exit(1);
277 		error = editor(lp, f, specname, fstabfile);
278 		break;
279 	case READ:
280 		if (argc != 1)
281 			usage();
282 		if ((lp = readlabel(f)) == NULL)
283 			exit(1);
284 		if (tflag)
285 			makedisktab(stdout, lp);
286 		else
287 			display(stdout, lp);
288 		error = checklabel(lp);
289 		break;
290 	case RESTORE:
291 		if (argc < 2 || argc > 3)
292 			usage();
293 #if NUMBOOT > 0
294 		if (installboot && argc == 3)
295 			makelabel(argv[2], NULL, &lab);
296 #endif
297 		lp = makebootarea(bootarea, &lab, f);
298 		if (!(t = fopen(argv[1], "r")))
299 			err(4, "%s", argv[1]);
300 		if (getasciilabel(t, lp))
301 			error = writelabel(f, bootarea, lp);
302 		break;
303 	case SETWRITEABLE:
304 		if (!donothing) {
305 			if (ioctl(f, DIOCWLABEL, (char *)&writeable) < 0)
306 				err(4, "ioctl DIOCWLABEL");
307 		}
308 		break;
309 	case WRITE:
310 		if (argc < 2 || argc > 3)
311 			usage();
312 		makelabel(argv[1], argc == 3 ? argv[2] : NULL, &lab);
313 		lp = makebootarea(bootarea, &lab, f);
314 		*lp = lab;
315 		if (checklabel(lp) == 0)
316 			error = writelabel(f, bootarea, lp);
317 		break;
318 #if NUMBOOT > 0
319 	case WRITEBOOT:
320 	{
321 		struct disklabel tlab;
322 
323 		if ((lp = readlabel(f)) == NULL)
324 			exit(1);
325 		tlab = *lp;
326 		if (argc == 2)
327 			makelabel(argv[1], NULL, &lab);
328 		lp = makebootarea(bootarea, &lab, f);
329 		*lp = tlab;
330 		if (checklabel(lp) == 0)
331 			error = writelabel(f, bootarea, lp);
332 		break;
333 	}
334 #endif
335 	default:
336 		break;
337 	}
338 	exit(error);
339 }
340 
341 /*
342  * Construct a prototype disklabel from /etc/disktab.  As a side
343  * effect, set the names of the primary and secondary boot files
344  * if specified.
345  */
346 void
347 makelabel(type, name, lp)
348 	char *type, *name;
349 	struct disklabel *lp;
350 {
351 	struct disklabel *dp;
352 
353 	dp = getdiskbyname(type);
354 	if (dp == NULL)
355 		errx(1, "unknown disk type: %s", type);
356 	*lp = *dp;
357 #if NUMBOOT > 0
358 	/*
359 	 * Set bootstrap name(s).
360 	 * 1. If set from command line, use those,
361 	 * 2. otherwise, check if disktab specifies them (b0 or b1),
362 	 * 3. otherwise, makebootarea() will choose ones based on the name
363 	 *    of the disk special file. E.g. /dev/ra0 -> raboot, bootra
364 	 */
365 	if (!xxboot && lp->d_boot0) {
366 		if (*lp->d_boot0 != '/')
367 			(void)sprintf(boot0, "%s%s",
368 				      _PATH_BOOTDIR, lp->d_boot0);
369 		else
370 			(void)strcpy(boot0, lp->d_boot0);
371 		xxboot = boot0;
372 	}
373 #if NUMBOOT > 1
374 	if (!bootxx && lp->d_boot1) {
375 		if (*lp->d_boot1 != '/')
376 			(void)sprintf(boot1, "%s%s",
377 				      _PATH_BOOTDIR, lp->d_boot1);
378 		else
379 			(void)strcpy(boot1, lp->d_boot1);
380 		bootxx = boot1;
381 	}
382 #endif
383 #endif
384 	/* d_packname is union d_boot[01], so zero */
385 	memset(lp->d_packname, 0, sizeof(lp->d_packname));
386 	if (name)
387 		(void)strncpy(lp->d_packname, name, sizeof(lp->d_packname));
388 }
389 
390 int
391 writelabel(f, boot, lp)
392 	int f;
393 	char *boot;
394 	struct disklabel *lp;
395 {
396 	int writeable;
397 	off_t sectoffset = 0;
398 
399 	if (nwflag) {
400 		warnx("DANGER! The disklabel was not found at the correct location!");
401 		warnx("To repair this situation, use `disklabel %s > file' to",
402 		    dkname);
403 		warnx("save it, then use `disklabel -R %s file' to replace it.",
404 		    dkname);
405 		warnx("A new disklabel is not being installed now.");
406 		return(0); /* Actually 1 but we want to exit */
407 	}
408 #if NUMBOOT > 0
409 	setbootflag(lp);
410 #endif
411 	lp->d_magic = DISKMAGIC;
412 	lp->d_magic2 = DISKMAGIC;
413 	lp->d_checksum = 0;
414 	lp->d_checksum = dkcksum(lp);
415 	if (rflag) {
416 #ifdef DOSLABEL
417 		struct partition *pp = &lp->d_partitions[2];
418 
419 		/*
420 		 * If OpenBSD DOS partition is missing, or if
421 		 * the label to be written is not within partition,
422 		 * prompt first. Need to allow this in case operator
423 		 * wants to convert the drive for dedicated use.
424 		 * In this case, partition 'a' had better start at 0,
425 		 * otherwise we reject the request as meaningless. -wfj
426 		 */
427 		if (dosdp && pp->p_size &&
428 		    (dosdp->dp_typ == DOSPTYP_OPENBSD ||
429 		    dosdp->dp_typ == DOSPTYP_FREEBSD ||
430 		    dosdp->dp_typ == DOSPTYP_NETBSD)) {
431 		        sectoffset = (off_t)get_le(&dosdp->dp_start) *
432 			    lp->d_secsize;
433 		} else {
434 			if (dosdp) {
435 				int first, ch;
436 
437 				printf("Erase the previous contents of the disk? [n]: ");
438 				fflush(stdout);
439 				first = ch = getchar();
440 				while (ch != '\n' && ch != EOF)
441 					ch = getchar();
442 				if (first != 'y' && first != 'Y')
443 					exit(0);
444 			}
445 			sectoffset = 0;
446 		}
447 
448 #if NUMBOOT > 0
449 		/*
450 		 * If we are not installing a boot program
451 		 * we must read the current bootarea so we don't
452 		 * clobber the existing boot.
453 		 */
454 		if (!installboot) {
455 			struct disklabel tlab;
456 
457 			if (lseek(f, sectoffset, SEEK_SET) < 0) {
458 				perror("lseek");
459 				return (1);
460 			}
461 			tlab = *lp;
462 			if (read(f, boot, tlab.d_bbsize) != tlab.d_bbsize) {
463 				perror("read");
464 				return (1);
465 			}
466 			*lp =tlab;
467 		}
468 #endif
469 #endif
470 
471 		/*
472 		 * First set the kernel disk label,
473 		 * then write a label to the raw disk.
474 		 * If the SDINFO ioctl fails because it is unimplemented,
475 		 * keep going; otherwise, the kernel consistency checks
476 		 * may prevent us from changing the current (in-core)
477 		 * label.
478 		 */
479 		if (!donothing) {
480 			if (ioctl(f, DIOCSDINFO, lp) < 0 &&
481 			    errno != ENODEV && errno != ENOTTY) {
482 				l_perror("ioctl DIOCSDINFO");
483 				return (1);
484 			}
485 		}
486 		if (verbose)
487 			printf("writing label to block %lld (0x%qx)\n",
488 			    (long long)sectoffset/DEV_BSIZE,
489 			    (long long)sectoffset/DEV_BSIZE);
490 		if (!donothing) {
491 			if (lseek(f, sectoffset, SEEK_SET) < 0) {
492 				perror("lseek");
493 				return (1);
494 			}
495 			/*
496 			 * write enable label sector before write (if necessary),
497 			 * disable after writing.
498 			 */
499 			writeable = 1;
500 
501 			if (ioctl(f, DIOCWLABEL, &writeable) < 0)
502 				perror("ioctl DIOCWLABEL");
503 #ifdef __alpha__
504 			/*
505 			 * The Alpha requires that the boot block be checksummed.
506 			 * The first 63 8-byte quantites are summed into the 64th.
507 			 */
508 			{
509 				int i;
510 				u_int64_t *dp, sum;
511 
512 				dp = (u_int64_t *)boot;
513 				sum = 0;
514 				for (i = 0; i < 63; i++)
515 					sum += dp[i];
516 				dp[63] = sum;
517 			}
518 #endif
519 			if (write(f, boot, lp->d_bbsize) != lp->d_bbsize) {
520 				perror("write");
521 				return (1);
522 			}
523 		}
524 #if NUMBOOT > 0
525 		/*
526 		 * Output the remainder of the disklabel
527 		 */
528 		if (!donothing && bootbuf && write(f, bootbuf, bootsize) != bootsize) {
529 			perror("write");
530 			return(1);
531 		}
532 #endif
533 		writeable = 0;
534 		if (!donothing)
535 			if (ioctl(f, DIOCWLABEL, &writeable) < 0)
536 				perror("ioctl DIOCWLABEL");
537 	} else {
538 		if (!donothing) {
539 			if (ioctl(f, DIOCWDINFO, lp) < 0) {
540 				l_perror("ioctl DIOCWDINFO");
541 				return (1);
542 			}
543 		}
544 	}
545 #ifdef __vax__
546 	if (lp->d_type == DTYPE_SMD && lp->d_flags & D_BADSECT) {
547 		daddr_t alt;
548 		int i;
549 
550 		alt = lp->d_ncylinders * lp->d_secpercyl - lp->d_nsectors;
551 		for (i = 1; i < 11 && i < lp->d_nsectors; i += 2) {
552 			(void)lseek(f, (off_t)((alt + i) * lp->d_secsize),
553 			    SEEK_SET);
554 			if (!donothing)
555 				if (write(f, boot, lp->d_secsize) < lp->d_secsize)
556 					warn("alternate label %d write", i/2);
557 		}
558 	}
559 #endif
560 	return (0);
561 }
562 
563 void
564 l_perror(s)
565 	char *s;
566 {
567 
568 	switch (errno) {
569 	case ESRCH:
570 		warnx("%s: No disk label on disk;\n"
571 		    "use \"disklabel -r\" to install initial label", s);
572 		break;
573 	case EINVAL:
574 		warnx("%s: Label magic number or checksum is wrong!\n"
575 		    "(disklabel or kernel is out of date?)", s);
576 		break;
577 	case EBUSY:
578 		warnx("%s: Open partition would move or shrink", s);
579 		break;
580 	case EXDEV:
581 		warnx("%s: Labeled partition or 'a' partition must start "
582 		    "at beginning of disk", s);
583 		break;
584 	default:
585 		warn("%s", s);
586 		break;
587 	}
588 }
589 
590 #ifdef DOSLABEL
591 /*
592  * Fetch DOS partition table from disk.
593  */
594 struct dos_partition *
595 readmbr(f)
596 	int f;
597 {
598 	static int mbr[DEV_BSIZE / sizeof(int)];
599 	struct dos_partition *dp;
600 	u_int16_t signature;
601 	int part;
602 
603 	/*
604 	 * This must be done this way due to alignment restrictions
605 	 * in for example mips processors.
606          */
607 	dp = (struct dos_partition *)mbr;
608 	if (lseek(f, (off_t)DOSBBSECTOR * DEV_BSIZE, SEEK_SET) < 0 ||
609 	    read(f, mbr, sizeof(mbr)) < sizeof(mbr))
610 		err(4, "can't read master boot record");
611 	signature = *((u_char *)mbr + DOSMBR_SIGNATURE_OFF) |
612 	    (*((u_char *)mbr + DOSMBR_SIGNATURE_OFF + 1) << 8);
613 	bcopy((char *)mbr+DOSPARTOFF, (char *)mbr, sizeof(*dp) * NDOSPART);
614 
615 	/*
616 	 * Don't (yet) know disk geometry (BIOS), use
617 	 * partition table to find OpenBSD partition, and obtain
618 	 * disklabel from there.
619 	 */
620 	/* Check if table is valid. */
621 	for (part = 0; part < NDOSPART; part++) {
622 		if ((dp[part].dp_flag & ~0x80) != 0)
623 			return (0);
624 	}
625 	/* Find OpenBSD partition. */
626 	for (part = 0; part < NDOSPART; part++) {
627 		if (get_le(&dp[part].dp_size) && dp[part].dp_typ == DOSPTYP_OPENBSD) {
628 			fprintf(stderr, "# using MBR partition %d: "
629 			    "type %02X off %d (0x%x) size %d (0x%x)\n", part,
630 			    dp[part].dp_typ,
631 			    get_le(&dp[part].dp_start), get_le(&dp[part].dp_start),
632 			    get_le(&dp[part].dp_size), get_le(&dp[part].dp_size));
633 			return (&dp[part]);
634 		}
635 	}
636 	for (part = 0; part < NDOSPART; part++) {
637 		if (get_le(&dp[part].dp_size) && dp[part].dp_typ == DOSPTYP_FREEBSD) {
638 			fprintf(stderr, "# using MBR partition %d: "
639 			    "type %02X off %d (0x%x) size %d (0x%x)\n", part,
640 			    dp[part].dp_typ,
641 			    get_le(&dp[part].dp_start), get_le(&dp[part].dp_start),
642 			    get_le(&dp[part].dp_size), get_le(&dp[part].dp_size));
643 			return (&dp[part]);
644 		}
645 	}
646 	for (part = 0; part < NDOSPART; part++) {
647 		if (get_le(&dp[part].dp_size) && dp[part].dp_typ == DOSPTYP_NETBSD) {
648 			fprintf(stderr, "# using MBR partition %d: "
649 			    "type %02X off %d (0x%x) size %d (0x%x)\n", part,
650 			    dp[part].dp_typ,
651 			    get_le(&dp[part].dp_start), get_le(&dp[part].dp_start),
652 			    get_le(&dp[part].dp_size), get_le(&dp[part].dp_size));
653 			return (&dp[part]);
654 		}
655 	}
656 
657 	/*
658 	 * If there is no signature and no OpenBSD partition this is probably
659 	 * not an MBR.
660 	 */
661 	if (signature != DOSMBR_SIGNATURE)
662 		return (NULL);
663 
664 	/* If no OpenBSD partition, find first used partition. */
665 	for (part = 0; part < NDOSPART; part++) {
666 		if (get_le(&dp[part].dp_size)) {
667 			warnx("warning, DOS partition table with no valid OpenBSD partition");
668 			return (&dp[part]);
669 		}
670 	}
671 	/* Table appears to be empty. */
672 	return (NULL);
673 }
674 #endif
675 
676 /*
677  * Fetch disklabel for disk.
678  * Use ioctl to get label unless -r flag is given.
679  */
680 struct disklabel *
681 readlabel(f)
682 	int f;
683 {
684 	struct disklabel *lp = NULL;
685 
686 	if (rflag) {
687 		char *msg;
688 		off_t sectoffset = 0;
689 
690 #ifdef DOSLABEL
691 		if (dosdp && get_le(&dosdp->dp_size) &&
692 		    (dosdp->dp_typ == DOSPTYP_OPENBSD ||
693 		    dosdp->dp_typ == DOSPTYP_FREEBSD ||
694 		    dosdp->dp_typ == DOSPTYP_NETBSD))
695 			sectoffset = (off_t)get_le(&dosdp->dp_start) *
696 			    DEV_BSIZE;
697 #endif
698 		if (verbose)
699 			printf("reading label from block %lld, offset %lld\n",
700 			    (long long)sectoffset/DEV_BSIZE,
701 			    sectoffset/DEV_BSIZE +
702 			    (LABELSECTOR * DEV_BSIZE) + LABELOFFSET);
703 		if (lseek(f, sectoffset, SEEK_SET) < 0 ||
704 		    read(f, bootarea, BBSIZE) < BBSIZE)
705 			err(4, "%s", specname);
706 
707 		lp = (struct disklabel *)(bootarea +
708 			(LABELSECTOR * DEV_BSIZE) + LABELOFFSET);
709 		if (lp->d_magic == DISKMAGIC &&
710 		    lp->d_magic2 == DISKMAGIC) {
711 			if (lp->d_npartitions <= MAXPARTITIONS &&
712 			    dkcksum(lp) == 0)
713 				return (lp);
714 
715 			msg = "disk label corrupted";
716 		}
717 		else {
718 			warnx("no disklabel found. scanning.");
719 		}
720 		nwflag++;
721 
722 		msg = "no disk label";
723 		for (lp = (struct disklabel *)bootarea;
724 		    lp <= (struct disklabel *)(bootarea + BBSIZE - sizeof(*lp));
725 		    lp = (struct disklabel *)((char *)lp + sizeof(long))) {
726 			if (lp->d_magic == DISKMAGIC &&
727 			    lp->d_magic2 == DISKMAGIC) {
728 				if (lp->d_npartitions <= MAXPARTITIONS &&
729 				    dkcksum(lp) == 0) {
730 					warnx("found at 0x%lx",
731 					    (long)((char *)lp - bootarea));
732 					return (lp);
733 				}
734 				msg = "disk label corrupted";
735 			}
736 		}
737 		warnx("%s", msg);
738 		return(NULL);
739 	} else {
740 		if (cflag && ioctl(f, DIOCRLDINFO) < 0)
741 			err(4, "ioctl DIOCRLDINFO");
742 		if (dflag) {
743 			lp = &lab;
744 			if (ioctl(f, DIOCGPDINFO, lp) < 0)
745 				err(4, "ioctl DIOCGPDINFO");
746 		} else {
747 			lp = &lab;
748 			if (ioctl(f, DIOCGDINFO, lp) < 0)
749 				err(4, "ioctl DIOCGDINFO");
750 		}
751 	}
752 	return (lp);
753 }
754 
755 /*
756  * Construct a bootarea (d_bbsize bytes) in the specified buffer ``boot''
757  * Returns a pointer to the disklabel portion of the bootarea.
758  */
759 struct disklabel *
760 makebootarea(boot, dp, f)
761 	char *boot;
762 	struct disklabel *dp;
763 	int f;
764 {
765 	struct disklabel *lp;
766 	char *p;
767 #if NUMBOOT > 0
768 	char *dkbasename;
769 	int b;
770 #if NUMBOOT == 1
771 	struct stat sb;
772 #endif
773 #endif
774 
775 	/* XXX */
776 	if (dp->d_secsize == 0) {
777 		dp->d_secsize = DEV_BSIZE;
778 		dp->d_bbsize = BBSIZE;
779 	}
780 	lp = (struct disklabel *)
781 	    (boot + (LABELSECTOR * dp->d_secsize) + LABELOFFSET);
782 	memset(lp, 0, sizeof *lp);
783 #if NUMBOOT > 0
784 	/*
785 	 * If we are not installing a boot program but we are installing a
786 	 * label on disk then we must read the current bootarea so we don't
787 	 * clobber the existing boot.
788 	 */
789 	if (!installboot) {
790 #ifndef __i386__
791 		if (rflag) {
792 			if (read(f, boot, BBSIZE) < BBSIZE)
793 				err(4, "%s", specname);
794 			memset(lp, 0, sizeof *lp);
795 		}
796 #endif
797 		return (lp);
798 	}
799 	/*
800 	 * We are installing a boot program.  Determine the name(s) and
801 	 * read them into the appropriate places in the boot area.
802 	 */
803 	if (!xxboot || !bootxx) {
804 		dkbasename = np;
805 		if ((p = strrchr(dkname, '/')) == NULL)
806 			p = dkname;
807 		else
808 			p++;
809 		while (*p && !isdigit(*p))
810 			*np++ = *p++;
811 		*np++ = '\0';
812 
813 		if (!xxboot) {
814 			(void)sprintf(np, "%s%sboot",
815 				      _PATH_BOOTDIR, dkbasename);
816 			if (access(np, F_OK) < 0 && dkbasename[0] == 'r')
817 				dkbasename++;
818 			xxboot = np;
819 			(void)sprintf(xxboot, "%s%sboot",
820 				      _PATH_BOOTDIR, dkbasename);
821 			np += strlen(xxboot) + 1;
822 		}
823 #if NUMBOOT > 1
824 		if (!bootxx) {
825 			(void)sprintf(np, "%sboot%s",
826 				      _PATH_BOOTDIR, dkbasename);
827 			if (access(np, F_OK) < 0 && dkbasename[0] == 'r')
828 				dkbasename++;
829 			bootxx = np;
830 			(void)sprintf(bootxx, "%sboot%s",
831 				      _PATH_BOOTDIR, dkbasename);
832 			np += strlen(bootxx) + 1;
833 		}
834 #endif
835 	}
836 	if (verbose)
837 		warnx("bootstraps: xxboot = %s, bootxx = %s", xxboot,
838 		    bootxx ? bootxx : "NONE");
839 
840 	/*
841 	 * Strange rules:
842 	 * 1. One-piece bootstrap (hp300/hp800)
843 	 *	up to d_bbsize bytes of ``xxboot'' go in bootarea, the rest
844 	 *	is remembered and written later following the bootarea.
845 	 * 2. Two-piece bootstraps (vax/i386?/mips?)
846 	 *	up to d_secsize bytes of ``xxboot'' go in first d_secsize
847 	 *	bytes of bootarea, remaining d_bbsize-d_secsize filled
848 	 *	from ``bootxx''.
849 	 */
850 	b = open(xxboot, O_RDONLY);
851 	if (b < 0)
852 		err(4, "%s", xxboot);
853 #if NUMBOOT > 1
854 	if (read(b, boot, (int)dp->d_secsize) < 0)
855 		err(4, "%s", xxboot);
856 	(void)close(b);
857 	b = open(bootxx, O_RDONLY);
858 	if (b < 0)
859 		err(4, "%s", bootxx);
860 	if (read(b, &boot[dp->d_secsize], (int)(dp->d_bbsize-dp->d_secsize)) < 0)
861 		err(4, "%s", bootxx);
862 #else
863 	if (read(b, boot, (int)dp->d_bbsize) < 0)
864 		err(4, "%s", xxboot);
865 	(void)fstat(b, &sb);
866 	bootsize = (int)sb.st_size - dp->d_bbsize;
867 	if (bootsize > 0) {
868 		/* XXX assume d_secsize is a power of two */
869 		bootsize = (bootsize + dp->d_secsize-1) & ~(dp->d_secsize-1);
870 		bootbuf = (char *)malloc((size_t)bootsize);
871 		if (bootbuf == 0)
872 			err(4, "%s", xxboot);
873 		if (read(b, bootbuf, bootsize) < 0) {
874 			free(bootbuf);
875 			err(4, "%s", xxboot);
876 		}
877 	}
878 #endif
879 	(void)close(b);
880 #endif
881 	/*
882 	 * Make sure no part of the bootstrap is written in the area
883 	 * reserved for the label.
884 	 */
885 	for (p = (char *)lp; p < (char *)lp + sizeof(struct disklabel); p++)
886 		if (*p)
887 			errx(2, "Bootstrap doesn't leave room for disk label");
888 	return (lp);
889 }
890 
891 void
892 makedisktab(f, lp)
893 	FILE *f;
894 	struct disklabel *lp;
895 {
896 	int i;
897 	char *did = "\\\n\t:";
898 	struct partition *pp;
899 
900 	if (lp->d_packname[0])
901 		(void)fprintf(f, "%.*s|", (int)sizeof(lp->d_packname),
902 		    lp->d_packname);
903 	if (lp->d_typename[0])
904 		(void)fprintf(f, "%.*s|", (int)sizeof(lp->d_typename),
905 		    lp->d_typename);
906 	(void)fputs("Automatically generated label:\\\n\t:dt=", f);
907 	if ((unsigned) lp->d_type < DKMAXTYPES)
908 		(void)fprintf(f, "%s:", dktypenames[lp->d_type]);
909 	else
910 		(void)fprintf(f, "unknown%d:", lp->d_type);
911 
912 	(void)fprintf(f, "se#%u:", lp->d_secsize);
913 	(void)fprintf(f, "ns#%u:", lp->d_nsectors);
914 	(void)fprintf(f, "nt#%u:", lp->d_ntracks);
915 	(void)fprintf(f, "nc#%u:", lp->d_ncylinders);
916 	(void)fprintf(f, "sc#%u:", lp->d_secpercyl);
917 	(void)fprintf(f, "su#%u:", lp->d_secperunit);
918 
919 	if (lp->d_rpm != 3600) {
920 		(void)fprintf(f, "%srm#%d:", did, lp->d_rpm);
921 		did = "";
922 	}
923 	if (lp->d_interleave != 1) {
924 		(void)fprintf(f, "%sil#%d:", did, lp->d_interleave);
925 		did = "";
926 	}
927 	if (lp->d_trackskew != 0) {
928 		(void)fprintf(f, "%ssk#%d:", did, lp->d_trackskew);
929 		did = "";
930 	}
931 	if (lp->d_cylskew != 0) {
932 		(void)fprintf(f, "%scs#%d:", did, lp->d_cylskew);
933 		did = "";
934 	}
935 	if (lp->d_headswitch != 0) {
936 		(void)fprintf(f, "%shs#%u:", did, lp->d_headswitch);
937 		did = "";
938 	}
939 	if (lp->d_trkseek != 0) {
940 		(void)fprintf(f, "%sts#%u:", did, lp->d_trkseek);
941 		did = "";
942 	}
943 	for (i = 0; i < NDDATA; i++)
944 		if (lp->d_drivedata[i])
945 			(void)fprintf(f, "d%d#%u", i, lp->d_drivedata[i]);
946 	pp = lp->d_partitions;
947 	for (i = 0; i < lp->d_npartitions; i++, pp++) {
948 		if (pp->p_size) {
949 			char c = 'a' + i;
950 
951 			(void)fprintf(f, "\\\n\t:");
952 			(void)fprintf(f, "p%c#%u:", c, pp->p_size);
953 			(void)fprintf(f, "o%c#%u:", c, pp->p_offset);
954 			if (pp->p_fstype != FS_UNUSED) {
955 				if ((unsigned) pp->p_fstype < FSMAXTYPES)
956 					(void)fprintf(f, "t%c=%s:", c,
957 					    fstypenames[pp->p_fstype]);
958 				else
959 					(void)fprintf(f, "t%c=unknown%d:",
960 					    c, pp->p_fstype);
961 			}
962 			switch (pp->p_fstype) {
963 
964 			case FS_UNUSED:
965 				break;
966 
967 			case FS_BSDFFS:
968 				(void)fprintf(f, "b%c#%u:", c,
969 				    pp->p_fsize * pp->p_frag);
970 				(void)fprintf(f, "f%c#%u:", c, pp->p_fsize);
971 				break;
972 
973 			default:
974 				break;
975 			}
976 		}
977 	}
978 	(void)fputc('\n', f);
979 	(void)fflush(f);
980 }
981 
982 int
983 width_partition(lp, unit)
984 	struct disklabel *lp;
985 {
986 	unit = toupper(unit);
987 	switch (unit) {
988 	case 'K':
989 		return 10;
990 	}
991 	return 8;
992 }
993 
994 /*
995  * Display a particular partion.
996  */
997 void
998 display_partition(f, lp, mp, i, unit, width)
999 	FILE *f;
1000 	struct disklabel *lp;
1001 	char **mp;
1002 	int i;
1003 	char unit;
1004 	int width;
1005 {
1006 	struct partition *pp = &lp->d_partitions[i];
1007 	double p_size, p_offset;
1008 
1009 	if (width == 0)
1010 		width = 8;
1011 	unit = toupper(unit);
1012 	switch (unit) {
1013 	case 'B':
1014 		p_size = (double)pp->p_size * lp->d_secsize;
1015 		p_offset = (double)pp->p_offset * lp->d_secsize;
1016 		break;
1017 
1018 	case 'C':
1019 		p_size = (double)pp->p_size / lp->d_secpercyl;
1020 		p_offset = (double)pp->p_offset / lp->d_secpercyl;
1021 		break;
1022 
1023 	case 'K':
1024 		p_size = (double)pp->p_size / (1024 / lp->d_secsize);
1025 		p_offset = (double)pp->p_offset / (1024 / lp->d_secsize);
1026 		break;
1027 
1028 	case 'M':
1029 		p_size = (double)pp->p_size / ((1024*1024) / lp->d_secsize);
1030 		p_offset = (double)pp->p_offset / ((1024*1024) / lp->d_secsize);
1031 		break;
1032 
1033 	case 'G':
1034 		p_size = (double)pp->p_size / ((1024*1024*1024) / lp->d_secsize);
1035 		p_offset = (double)pp->p_offset / ((1024*1024*1024) / lp->d_secsize);
1036 		break;
1037 
1038 	default:
1039 		p_size = -1;			/* no conversion */
1040 		p_offset = 0;
1041 		break;
1042 	}
1043 
1044 	if (pp->p_size) {
1045 		if (p_size < 0)
1046 			fprintf(f, "  %c: %*u %*u  ", 'a' + i,
1047 			    width, pp->p_size, width, pp->p_offset);
1048 		else
1049 			fprintf(f, "  %c: %*.1f%c %*.1f%c  ", 'a' + i,
1050 			    width-1, p_size, unit, width-1, p_offset, unit);
1051 		if ((unsigned) pp->p_fstype < FSMAXTYPES)
1052 			fprintf(f, "%8.8s", fstypenames[pp->p_fstype]);
1053 		else
1054 			fprintf(f, "%8d", pp->p_fstype);
1055 		switch (pp->p_fstype) {
1056 
1057 		case FS_UNUSED:				/* XXX */
1058 			fprintf(f, "    %5d %5d %5.5s ",
1059 			    pp->p_fsize, pp->p_fsize * pp->p_frag, "");
1060 			break;
1061 
1062 		case FS_BSDFFS:
1063 			fprintf(f, "    %5d %5d %5d ",
1064 			    pp->p_fsize, pp->p_fsize * pp->p_frag,
1065 			    pp->p_cpg);
1066 			break;
1067 
1068 		default:
1069 			fprintf(f, "%22.22s", "");
1070 			break;
1071 		}
1072 		if (mp != NULL) {
1073 			if (mp[i] != NULL)
1074 				fprintf(f, " # %s", mp[i]);
1075 		} else if (lp->d_secpercyl) {
1076 			fprintf(f, "\t# (Cyl. %4d",
1077 			    pp->p_offset / lp->d_secpercyl);
1078 			if (pp->p_offset % lp->d_secpercyl)
1079 				putc('*', f);
1080 			else
1081 				putc(' ', f);
1082 			fprintf(f, "- %u",
1083 			    (pp->p_offset +
1084 			    pp->p_size + lp->d_secpercyl - 1) /
1085 			    lp->d_secpercyl - 1);
1086 			if ((pp->p_offset + pp->p_size) % lp->d_secpercyl)
1087 				putc('*', f);
1088 			putc(')', f);
1089 		}
1090 		putc('\n', f);
1091 	}
1092 }
1093 
1094 void
1095 display(f, lp)
1096 	FILE *f;
1097 	struct disklabel *lp;
1098 {
1099 	int i, j;
1100 	int width;
1101 
1102 	fprintf(f, "# %s:\n", specname);
1103 	if ((unsigned) lp->d_type < DKMAXTYPES)
1104 		fprintf(f, "type: %s\n", dktypenames[lp->d_type]);
1105 	else
1106 		fprintf(f, "type: %d\n", lp->d_type);
1107 	fprintf(f, "disk: %.*s\n", (int)sizeof(lp->d_typename), lp->d_typename);
1108 	fprintf(f, "label: %.*s\n", (int)sizeof(lp->d_packname), lp->d_packname);
1109 	fprintf(f, "flags:");
1110 	if (lp->d_flags & D_REMOVABLE)
1111 		fprintf(f, " removable");
1112 	if (lp->d_flags & D_ECC)
1113 		fprintf(f, " ecc");
1114 	if (lp->d_flags & D_BADSECT)
1115 		fprintf(f, " badsect");
1116 	putc('\n', f);
1117 	fprintf(f, "bytes/sector: %ld\n", (long)lp->d_secsize);
1118 	fprintf(f, "sectors/track: %ld\n", (long)lp->d_nsectors);
1119 	fprintf(f, "tracks/cylinder: %ld\n", (long)lp->d_ntracks);
1120 	fprintf(f, "sectors/cylinder: %ld\n", (long)lp->d_secpercyl);
1121 	fprintf(f, "cylinders: %ld\n", (long)lp->d_ncylinders);
1122 	fprintf(f, "total sectors: %ld\n", (long)lp->d_secperunit);
1123 	fprintf(f, "rpm: %ld\n", (long)lp->d_rpm);
1124 	fprintf(f, "interleave: %ld\n", (long)lp->d_interleave);
1125 	fprintf(f, "trackskew: %ld\n", (long)lp->d_trackskew);
1126 	fprintf(f, "cylinderskew: %ld\n", (long)lp->d_cylskew);
1127 	fprintf(f, "headswitch: %ld\t\t# microseconds\n",
1128 	    (long)lp->d_headswitch);
1129 	fprintf(f, "track-to-track seek: %ld\t# microseconds\n",
1130 	    (long)lp->d_trkseek);
1131 	fprintf(f, "drivedata: ");
1132 	for (i = NDDATA - 1; i >= 0; i--)
1133 		if (lp->d_drivedata[i])
1134 			break;
1135 	if (i < 0)
1136 		i = 0;
1137 	for (j = 0; j <= i; j++)
1138 		fprintf(f, "%d ", lp->d_drivedata[j]);
1139 	fprintf(f, "\n\n%d partitions:\n", lp->d_npartitions);
1140 	width = width_partition(lp, 0);
1141 	fprintf(f,
1142 	    "#    %*.*s %*.*s    fstype   [fsize bsize   cpg]\n",
1143 	    width, width, "size", width, width, "offset");
1144 	for (i = 0; i < lp->d_npartitions; i++)
1145 		display_partition(f, lp, NULL, i, 0, width);
1146 	fflush(f);
1147 }
1148 
1149 int
1150 edit(lp, f)
1151 	struct disklabel *lp;
1152 	int f;
1153 {
1154 	int first, ch, fd;
1155 	struct disklabel label;
1156 	FILE *fp;
1157 
1158 	if ((fd = mkstemp(tmpfil)) == -1 || (fp = fdopen(fd, "w")) == NULL) {
1159 		if (fd != -1)
1160 			close(fd);
1161 		warn("%s", tmpfil);
1162 		return (1);
1163 	}
1164 	display(fp, lp);
1165 	fprintf(fp, "\n# Notes:\n");
1166 	fprintf(fp,
1167 "# Up to 16 partitions are valid, named from 'a' to 'p'.  Partition 'a' is\n"
1168 "# your root filesystem, 'b' is your swap, and 'c' should cover your whole\n"
1169 "# disk. Any other partition is free for any use.  'size' and 'offset' are\n"
1170 "# in 512-byte blocks. fstype should be '4.2BSD', 'swap', or 'none' or some\n"
1171 "# other values.  fsize/bsize/cpg should typically be '1024 8192 16' for a\n"
1172 "# 4.2BSD filesystem (or '512 4096 16' except on alpha, sun4, amiga, sun3...)\n");
1173 	fclose(fp);
1174 	for (;;) {
1175 		if (!editit())
1176 			break;
1177 		fp = fopen(tmpfil, "r");
1178 		if (fp == NULL) {
1179 			warn("%s", tmpfil);
1180 			break;
1181 		}
1182 		memset(&label, 0, sizeof(label));
1183 		if (getasciilabel(fp, &label)) {
1184 			if (cmplabel(lp, &label) == 0) {
1185 				puts("No changes.");
1186 				fclose(fp);
1187 				(void) unlink(tmpfil);
1188 				return (0);
1189 			}
1190 			*lp = label;
1191 			if (writelabel(f, bootarea, lp) == 0) {
1192 				fclose(fp);
1193 				(void) unlink(tmpfil);
1194 				return (0);
1195 			}
1196 		}
1197 		fclose(fp);
1198 		printf("re-edit the label? [y]: ");
1199 		fflush(stdout);
1200 		first = ch = getchar();
1201 		while (ch != '\n' && ch != EOF)
1202 			ch = getchar();
1203 		if (first == 'n' || first == 'N')
1204 			break;
1205 	}
1206 	(void)unlink(tmpfil);
1207 	return (1);
1208 }
1209 
1210 int
1211 editit()
1212 {
1213 	pid_t pid, xpid;
1214 	int stat;
1215 	extern char *getenv();
1216 	char *argp[] = {"sh", "-c", NULL, NULL};
1217 	char *ed, *p;
1218 
1219 	if ((ed = getenv("EDITOR")) == NULL)
1220 		ed = _PATH_VI;
1221 	p = (char *)malloc(strlen(ed) + 1 + strlen(tmpfil) + 1);
1222 	if (!p) {
1223 		warn("failed to start editor");
1224 		return (0);
1225 	}
1226 	sprintf(p, "%s %s", ed, tmpfil);
1227 	argp[2] = p;
1228 
1229 	/* Turn off signals. */
1230 	(void)signal(SIGHUP, SIG_IGN);
1231 	(void)signal(SIGINT, SIG_IGN);
1232 	(void)signal(SIGQUIT, SIG_IGN);
1233 	while ((pid = fork()) < 0) {
1234 		if (errno != EAGAIN) {
1235 			warn("fork");
1236 			free(p);
1237 			stat = 1;
1238 			goto bail;
1239 		}
1240 		sleep(1);
1241 	}
1242 	if (pid == 0) {
1243 		execv(_PATH_BSHELL, argp);
1244 		_exit(127);
1245 	}
1246 	free(p);
1247 	for (;;) {
1248 		xpid = waitpid(pid, (int *)&stat, WUNTRACED);
1249 		if (WIFSTOPPED(stat))
1250 			raise(WSTOPSIG(stat));
1251 		else if (WIFEXITED(stat))
1252 			break;
1253 	}
1254 bail:
1255 	(void)signal(SIGHUP, SIG_DFL);
1256 	(void)signal(SIGINT, SIG_DFL);
1257 	(void)signal(SIGQUIT, SIG_DFL);
1258 	return (!stat);
1259 }
1260 
1261 char *
1262 skip(cp)
1263 	char *cp;
1264 {
1265 
1266 	cp += strspn(cp, " \t");
1267 	if (*cp == '\0')
1268 		return (NULL);
1269 	return (cp);
1270 }
1271 
1272 char *
1273 word(cp)
1274 	char *cp;
1275 {
1276 
1277 	cp += strcspn(cp, " \t");
1278 	if (*cp == '\0')
1279 		return (NULL);
1280 	*cp++ = '\0';
1281 	cp += strspn(cp, " \t");
1282 	if (*cp == '\0')
1283 		return (NULL);
1284 	return (cp);
1285 }
1286 
1287 /*
1288  * Read an ascii label in from fd f,
1289  * in the same format as that put out by display(),
1290  * and fill in lp.
1291  */
1292 int
1293 getasciilabel(f, lp)
1294 	FILE *f;
1295 	struct disklabel *lp;
1296 {
1297 	char **cpp, *cp;
1298 	struct partition *pp;
1299 	char *tp, *s, line[BUFSIZ];
1300 	int v, lineno = 0, errors = 0;
1301 
1302 	lp->d_bbsize = BBSIZE;				/* XXX */
1303 	lp->d_sbsize = SBSIZE;				/* XXX */
1304 	while (fgets(line, sizeof(line) - 1, f)) {
1305 		lineno++;
1306 		if ((cp = strpbrk(line, "#\r\n")))
1307 			*cp = '\0';
1308 		cp = skip(line);
1309 		if (cp == NULL)
1310 			continue;
1311 		tp = strchr(cp, ':');
1312 		if (tp == NULL) {
1313 			warnx("line %d: syntax error", lineno);
1314 			errors++;
1315 			continue;
1316 		}
1317 		*tp++ = '\0', tp = skip(tp);
1318 		if (!strcmp(cp, "type")) {
1319 			if (tp == NULL)
1320 				tp = "unknown";
1321 			else if (strcasecmp(tp, "IDE") == 0)
1322 				tp = "ESDI";
1323 			cpp = dktypenames;
1324 			for (; cpp < &dktypenames[DKMAXTYPES]; cpp++)
1325 				if ((s = *cpp) && !strcasecmp(s, tp)) {
1326 					lp->d_type = cpp - dktypenames;
1327 					goto next;
1328 				}
1329 			v = atoi(tp);
1330 			if ((unsigned)v >= DKMAXTYPES)
1331 				warnx("line %d: warning, unknown disk type: %s",
1332 				    lineno, tp);
1333 			lp->d_type = v;
1334 			continue;
1335 		}
1336 		if (!strcmp(cp, "flags")) {
1337 			for (v = 0; (cp = tp) && *cp != '\0';) {
1338 				tp = word(cp);
1339 				if (!strcmp(cp, "removable"))
1340 					v |= D_REMOVABLE;
1341 				else if (!strcmp(cp, "ecc"))
1342 					v |= D_ECC;
1343 				else if (!strcmp(cp, "badsect"))
1344 					v |= D_BADSECT;
1345 				else {
1346 					warnx("line %d: bad flag: %s",
1347 					    lineno, cp);
1348 					errors++;
1349 				}
1350 			}
1351 			lp->d_flags = v;
1352 			continue;
1353 		}
1354 		if (!strcmp(cp, "drivedata")) {
1355 			int i;
1356 
1357 			for (i = 0; (cp = tp) && *cp != '\0' && i < NDDATA;) {
1358 				lp->d_drivedata[i++] = atoi(cp);
1359 				tp = word(cp);
1360 			}
1361 			continue;
1362 		}
1363 		if (sscanf(cp, "%d partitions", &v) == 1) {
1364 			if (v == 0 || (unsigned)v > MAXPARTITIONS) {
1365 				warnx("line %d: bad # of partitions", lineno);
1366 				lp->d_npartitions = MAXPARTITIONS;
1367 				errors++;
1368 			} else
1369 				lp->d_npartitions = v;
1370 			continue;
1371 		}
1372 		if (tp == NULL)
1373 			tp = "";
1374 		if (!strcmp(cp, "disk")) {
1375 			strncpy(lp->d_typename, tp, sizeof (lp->d_typename));
1376 			continue;
1377 		}
1378 		if (!strcmp(cp, "label")) {
1379 			strncpy(lp->d_packname, tp, sizeof (lp->d_packname));
1380 			continue;
1381 		}
1382 		if (!strcmp(cp, "bytes/sector")) {
1383 			v = atoi(tp);
1384 			if (v <= 0 || (v % 512) != 0) {
1385 				warnx("line %d: bad %s: %s", lineno, cp, tp);
1386 				errors++;
1387 			} else
1388 				lp->d_secsize = v;
1389 			continue;
1390 		}
1391 		if (!strcmp(cp, "sectors/track")) {
1392 			v = atoi(tp);
1393 			if (v <= 0) {
1394 				warnx("line %d: bad %s: %s", lineno, cp, tp);
1395 				errors++;
1396 			} else
1397 				lp->d_nsectors = v;
1398 			continue;
1399 		}
1400 		if (!strcmp(cp, "sectors/cylinder")) {
1401 			v = atoi(tp);
1402 			if (v <= 0) {
1403 				warnx("line %d: bad %s: %s", lineno, cp, tp);
1404 				errors++;
1405 			} else
1406 				lp->d_secpercyl = v;
1407 			continue;
1408 		}
1409 		if (!strcmp(cp, "tracks/cylinder")) {
1410 			v = atoi(tp);
1411 			if (v <= 0) {
1412 				warnx("line %d: bad %s: %s", lineno, cp, tp);
1413 				errors++;
1414 			} else
1415 				lp->d_ntracks = v;
1416 			continue;
1417 		}
1418 		if (!strcmp(cp, "cylinders")) {
1419 			v = atoi(tp);
1420 			if (v <= 0) {
1421 				warnx("line %d: bad %s: %s", lineno, cp, tp);
1422 				errors++;
1423 			} else
1424 				lp->d_ncylinders = v;
1425 			continue;
1426 		}
1427 		if (!strcmp(cp, "total sectors")) {
1428 			v = atoi(tp);
1429 			if (v <= 0) {
1430 				warnx("line %d: bad %s: %s", lineno, cp, tp);
1431 				errors++;
1432 			} else
1433 				lp->d_secperunit = v;
1434 			continue;
1435 		}
1436 		if (!strcmp(cp, "rpm")) {
1437 			v = atoi(tp);
1438 			if (v <= 0) {
1439 				warnx("line %d: bad %s: %s", lineno, cp, tp);
1440 				errors++;
1441 			} else
1442 				lp->d_rpm = v;
1443 			continue;
1444 		}
1445 		if (!strcmp(cp, "interleave")) {
1446 			v = atoi(tp);
1447 			if (v <= 0) {
1448 				warnx("line %d: bad %s: %s", lineno, cp, tp);
1449 				errors++;
1450 			} else
1451 				lp->d_interleave = v;
1452 			continue;
1453 		}
1454 		if (!strcmp(cp, "trackskew")) {
1455 			v = atoi(tp);
1456 			if (v < 0) {
1457 				warnx("line %d: bad %s: %s", lineno, cp, tp);
1458 				errors++;
1459 			} else
1460 				lp->d_trackskew = v;
1461 			continue;
1462 		}
1463 		if (!strcmp(cp, "cylinderskew")) {
1464 			v = atoi(tp);
1465 			if (v < 0) {
1466 				warnx("line %d: bad %s: %s", lineno, cp, tp);
1467 				errors++;
1468 			} else
1469 				lp->d_cylskew = v;
1470 			continue;
1471 		}
1472 		if (!strcmp(cp, "headswitch")) {
1473 			v = atoi(tp);
1474 			if (v < 0) {
1475 				warnx("line %d: bad %s: %s", lineno, cp, tp);
1476 				errors++;
1477 			} else
1478 				lp->d_headswitch = v;
1479 			continue;
1480 		}
1481 		if (!strcmp(cp, "track-to-track seek")) {
1482 			v = atoi(tp);
1483 			if (v < 0) {
1484 				warnx("line %d: bad %s: %s", lineno, cp, tp);
1485 				errors++;
1486 			} else
1487 				lp->d_trkseek = v;
1488 			continue;
1489 		}
1490 		if ('a' <= *cp && *cp <= 'z' && cp[1] == '\0') {
1491 			unsigned part = *cp - 'a';
1492 
1493 			if (part >= lp->d_npartitions) {
1494 				if (part >= MAXPARTITIONS) {
1495 					warnx("line %d: bad partition name: %s",
1496 					    lineno, cp);
1497 					errors++;
1498 					continue;
1499 				} else {
1500 					lp->d_npartitions = part + 1;
1501 				}
1502 			}
1503 			pp = &lp->d_partitions[part];
1504 #define NXTNUM(n) { \
1505 	if (tp == NULL) {					\
1506 		warnx("line %d: too few fields", lineno);	\
1507 		errors++;					\
1508 		break;						\
1509 	} else							\
1510 		cp = tp, tp = word(cp), (n) = atoi(cp);		\
1511 }
1512 			NXTNUM(v);
1513 			if (v < 0) {
1514 				warnx("line %d: bad partition size: %s",
1515 				    lineno, cp);
1516 				errors++;
1517 			} else
1518 				pp->p_size = v;
1519 			NXTNUM(v);
1520 			if (v < 0) {
1521 				warnx("line %d: bad partition offset: %s",
1522 				    lineno, cp);
1523 				errors++;
1524 			} else
1525 				pp->p_offset = v;
1526 			if (tp == NULL) {
1527 				pp->p_fstype = FS_UNUSED;
1528 				goto gottype;
1529 			}
1530 			cp = tp, tp = word(cp);
1531 			cpp = fstypenames;
1532 			for (; cpp < &fstypenames[FSMAXTYPES]; cpp++)
1533 				if ((s = *cpp) && !strcasecmp(s, cp)) {
1534 					pp->p_fstype = cpp - fstypenames;
1535 					goto gottype;
1536 				}
1537 			if (isdigit(*cp))
1538 				v = atoi(cp);
1539 			else
1540 				v = FSMAXTYPES;
1541 			if ((unsigned)v >= FSMAXTYPES) {
1542 				warnx("line %d: warning, unknown filesystem type: %s",
1543 				    lineno, cp);
1544 				v = FS_UNUSED;
1545 			}
1546 			pp->p_fstype = v;
1547 	gottype:
1548 			switch (pp->p_fstype) {
1549 
1550 			case FS_UNUSED:				/* XXX */
1551 				if (tp == NULL)	/* ok to skip fsize/bsize */
1552 					break;
1553 				NXTNUM(pp->p_fsize);
1554 				if (pp->p_fsize == 0)
1555 					break;
1556 				NXTNUM(v);
1557 				pp->p_frag = v / pp->p_fsize;
1558 				break;
1559 
1560 			case FS_BSDFFS:
1561 				NXTNUM(pp->p_fsize);
1562 				if (pp->p_fsize == 0)
1563 					break;
1564 				NXTNUM(v);
1565 				pp->p_frag = v / pp->p_fsize;
1566 				NXTNUM(pp->p_cpg);
1567 				break;
1568 
1569 			default:
1570 				break;
1571 			}
1572 			continue;
1573 		}
1574 		warnx("line %d: unknown field: %s", lineno, cp);
1575 		errors++;
1576 	next:
1577 		;
1578 	}
1579 	errors += checklabel(lp);
1580 	return (errors == 0);
1581 }
1582 
1583 /*
1584  * Check disklabel for errors and fill in
1585  * derived fields according to supplied values.
1586  */
1587 int
1588 checklabel(lp)
1589 	struct disklabel *lp;
1590 {
1591 	struct partition *pp;
1592 	int i, errors = 0;
1593 	char part;
1594 
1595 	if (lp->d_secsize == 0) {
1596 		warnx("sector size %d", lp->d_secsize);
1597 		return (1);
1598 	}
1599 	if (lp->d_nsectors == 0) {
1600 		warnx("sectors/track %d", lp->d_nsectors);
1601 		return (1);
1602 	}
1603 	if (lp->d_ntracks == 0) {
1604 		warnx("tracks/cylinder %d", lp->d_ntracks);
1605 		return (1);
1606 	}
1607 	if  (lp->d_ncylinders == 0) {
1608 		warnx("cylinders/unit %d", lp->d_ncylinders);
1609 		errors++;
1610 	}
1611 	if (lp->d_rpm == 0)
1612 		warnx("warning, revolutions/minute %d", lp->d_rpm);
1613 	if (lp->d_secpercyl == 0)
1614 		lp->d_secpercyl = lp->d_nsectors * lp->d_ntracks;
1615 	if (lp->d_secperunit == 0)
1616 		lp->d_secperunit = lp->d_secpercyl * lp->d_ncylinders;
1617 #ifdef i386__notyet
1618 	if (dosdp && dosdp->dp_size &&
1619 	    (dosdp->dp_typ == DOSPTYP_OPENBSD ||
1620 	    dosdp->dp_typ == DOSPTYP_FREEBSD ||
1621 	    dosdp->dp_typ == DOSPTYP_NETBSD)) {
1622 		&& lp->d_secperunit > dosdp->dp_start + dosdp->dp_size) {
1623 		warnx("exceeds DOS partition size");
1624 		errors++;
1625 		lp->d_secperunit = dosdp->dp_start + dosdp->dp_size;
1626 	}
1627 	/* XXX should also check geometry against BIOS's idea */
1628 #endif
1629 	if (lp->d_bbsize == 0) {
1630 		warnx("boot block size %d", lp->d_bbsize);
1631 		errors++;
1632 	} else if (lp->d_bbsize % lp->d_secsize)
1633 		warnx("warning, boot block size %% sector-size != 0");
1634 	if (lp->d_sbsize == 0) {
1635 		warnx("super block size %d", lp->d_sbsize);
1636 		errors++;
1637 	} else if (lp->d_sbsize % lp->d_secsize)
1638 		warnx("warning, super block size %% sector-size != 0");
1639 	if (lp->d_npartitions > MAXPARTITIONS)
1640 		warnx("warning, number of partitions (%d) > MAXPARTITIONS (%d)",
1641 		    lp->d_npartitions, MAXPARTITIONS);
1642 	for (i = 0; i < lp->d_npartitions; i++) {
1643 		part = 'a' + i;
1644 		pp = &lp->d_partitions[i];
1645 		if (pp->p_size == 0 && pp->p_offset != 0)
1646 			warnx("warning, partition %c: size 0, but offset %d",
1647 			    part, pp->p_offset);
1648 #ifdef CYLCHECK
1649 		if (pp->p_size % lp->d_secpercyl)
1650 			warnx("warning, partition %c: size %% cylinder-size != 0",
1651 			    part);
1652 		if (pp->p_offset % lp->d_secpercyl)
1653 			warnx("warning, partition %c: offset %% cylinder-size != 0",
1654 			    part);
1655 #endif
1656 #ifdef AAT0
1657 		if (i == 0 && pp->p_size != 0 && pp->p_offset != 0) {
1658 			warnx("this architecture requires partition 'a' to "
1659 			    "start at sector 0");
1660 			errors++;
1661 		}
1662 #endif
1663 		if (pp->p_offset > lp->d_secperunit) {
1664 			warnx("partition %c: offset past end of unit", part);
1665 			errors++;
1666 		}
1667 		if (pp->p_offset + pp->p_size > lp->d_secperunit) {
1668 			warnx("partition %c: partition extends past end of unit",
1669 			    part);
1670 			errors++;
1671 		}
1672 		if (pp->p_frag == 0 && pp->p_fsize != 0) {
1673 			warnx("partition %c: block size < fragment size", part);
1674 			errors++;
1675 		}
1676 	}
1677 	for (; i < MAXPARTITIONS; i++) {
1678 		part = 'a' + i;
1679 		pp = &lp->d_partitions[i];
1680 		if (pp->p_size || pp->p_offset)
1681 			warnx("warning, unused partition %c: size %d offset %d",
1682 			    'a' + i, pp->p_size, pp->p_offset);
1683 	}
1684 	return (errors);
1685 }
1686 
1687 #if NUMBOOT > 0
1688 /*
1689  * If we are installing a boot program that doesn't fit in d_bbsize
1690  * we need to mark those partitions that the boot overflows into.
1691  * This allows newfs to prevent creation of a filesystem where it might
1692  * clobber bootstrap code.
1693  */
1694 void
1695 setbootflag(lp)
1696 	struct disklabel *lp;
1697 {
1698 	struct partition *pp;
1699 	int i, errors = 0;
1700 	u_long boffset;
1701 	char part;
1702 
1703 	if (bootbuf == 0)
1704 		return;
1705 	boffset = bootsize / lp->d_secsize;
1706 	for (i = 0; i < lp->d_npartitions; i++) {
1707 		part = 'a' + i;
1708 		pp = &lp->d_partitions[i];
1709 		if (pp->p_size == 0)
1710 			continue;
1711 		if (boffset <= pp->p_offset) {
1712 			if (pp->p_fstype == FS_BOOT)
1713 				pp->p_fstype = FS_UNUSED;
1714 		} else if (pp->p_fstype != FS_BOOT) {
1715 			if (pp->p_fstype != FS_UNUSED) {
1716 				warnx("boot overlaps used partition %c", part);
1717 				errors++;
1718 			} else {
1719 				pp->p_fstype = FS_BOOT;
1720 				warnx("warning, boot overlaps partition %c, %s",
1721 				    part, "marked as FS_BOOT");
1722 			}
1723 		}
1724 	}
1725 	if (errors)
1726 		errx(4, "cannot install boot program");
1727 }
1728 #endif
1729 
1730 int
1731 cmplabel(lp1, lp2)
1732 	struct disklabel *lp1;
1733 	struct disklabel *lp2;
1734 {
1735 	struct disklabel lab1 = *lp1;
1736 	struct disklabel lab2 = *lp2;
1737 
1738 	/* We don't compare these fields */
1739 	lab1.d_magic = lab2.d_magic;
1740 	lab1.d_magic2 = lab2.d_magic2;
1741 	lab1.d_checksum = lab2.d_checksum;
1742 	lab1.d_bbsize = lab2.d_bbsize;
1743 	lab1.d_sbsize = lab2.d_sbsize;
1744 
1745 	return (memcmp(&lab1, &lab2, sizeof(struct disklabel)));
1746 }
1747 
1748 void
1749 usage()
1750 {
1751 	char *boot = "";
1752 	char blank[] = "                             ";
1753 
1754 #if NUMBOOT == 1
1755 	boot = " [-B [-b xxboot]]";
1756 #elif NUMBOOT == 2
1757 	boot = " [-B [-b xxboot [-s bootxx]]]";
1758 #endif
1759 	blank[strlen(boot)] = '\0';
1760 
1761 	fprintf(stderr, "usage:\n");
1762 	fprintf(stderr,
1763 	    "  disklabel [-nv] [-r|-cd] [-t] disk%s     (read)\n",
1764 	    blank);
1765 	fprintf(stderr,
1766 	    "  disklabel [-nv] [-r|-cd] -e disk%s       (edit)\n",
1767 	    blank);
1768 	fprintf(stderr,
1769 	    "  disklabel [-nv] [-r|-cd] [-f temp] -E disk%.*s  (simple editor)\n",
1770 	    (int)strlen(blank) - 5, blank);
1771 	fprintf(stderr,
1772 	    "  disklabel [-nv] [-r]%s -R disk proto     (restore)\n",
1773 	    boot);
1774 	fprintf(stderr,
1775 	    "  disklabel [-nv] [-r]%s -w disk dtab [id] (write)\n",
1776 	    boot);
1777 	fprintf(stderr,
1778 	    "  disklabel [-nv] [-N|-W] disk%s           (protect)\n", blank);
1779 	fprintf(stderr,
1780 	    "`disk' may be of the forms: sd0 or /dev/rsd0%c.\n", 'a'+RAW_PART);
1781 	fprintf(stderr,
1782 	    "`dtab' is an entry from %s, see disktab(5) for more info.\n",
1783 	    DISKTAB);
1784 	fprintf(stderr,
1785 	    "`proto' is the output from the read cmd form; -R is powerful.\n");
1786 #ifdef SEEALSO
1787 	fprintf(stderr,
1788 	    "For procedures specific to this architecture see: %s\n", SEEALSO);
1789 #endif
1790 	exit(1);
1791 }
1792