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