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