xref: /freebsd/sbin/bsdlabel/bsdlabel.c (revision 77b7cdf1999ee965ad494fddd184b18f532ac91a)
1 /*
2  * Copyright (c) 1994, 1995 Gordon W. Ross
3  * Copyright (c) 1994 Theo de Raadt
4  * All rights reserved.
5  * Copyright (c) 1987, 1993
6  *	The Regents of the University of California.  All rights reserved.
7  *
8  * This code is derived from software contributed to Berkeley by
9  * Symmetric Computer Systems.
10  *
11  * Redistribution and use in source and binary forms, with or without
12  * modification, are permitted provided that the following conditions
13  * are met:
14  * 1. Redistributions of source code must retain the above copyright
15  *    notice, this list of conditions and the following disclaimer.
16  * 2. Redistributions in binary form must reproduce the above copyright
17  *    notice, this list of conditions and the following disclaimer in the
18  *    documentation and/or other materials provided with the distribution.
19  * 3. All advertising materials mentioning features or use of this software
20  *    must display the following acknowledgement:
21  *	This product includes software developed by the University of
22  *	California, Berkeley and its contributors.
23  *      This product includes software developed by Theo de Raadt.
24  * 4. Neither the name of the University nor the names of its contributors
25  *    may be used to endorse or promote products derived from this software
26  *    without specific prior written permission.
27  *
28  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
29  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
30  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
31  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
32  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
33  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
34  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
35  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
36  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
37  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
38  * SUCH DAMAGE.
39  *
40  *	from: $NetBSD: disksubr.c,v 1.13 2000/12/17 22:39:18 pk $
41  */
42 
43 #ifndef lint
44 static const char copyright[] =
45 "@(#) Copyright (c) 1987, 1993\n\
46 	The Regents of the University of California.  All rights reserved.\n";
47 #endif /* not lint */
48 
49 #ifndef lint
50 #if 0
51 static char sccsid[] = "@(#)disklabel.c	8.2 (Berkeley) 1/7/94";
52 /* from static char sccsid[] = "@(#)disklabel.c	1.2 (Symmetric) 11/28/85"; */
53 #endif
54 #endif /* not lint */
55 
56 #include <sys/cdefs.h>
57 __FBSDID("$FreeBSD$");
58 
59 #include <sys/param.h>
60 #include <stdint.h>
61 #include <sys/file.h>
62 #include <sys/stat.h>
63 #include <sys/wait.h>
64 #include <sys/disk.h>
65 #define DKTYPENAMES
66 #define FSTYPENAMES
67 #include <sys/disklabel.h>
68 
69 #include <unistd.h>
70 #include <string.h>
71 #include <stdio.h>
72 #include <libgeom.h>
73 #include <stdlib.h>
74 #include <signal.h>
75 #include <stdarg.h>
76 #include <ctype.h>
77 #include <err.h>
78 #include <errno.h>
79 
80 #include "pathnames.h"
81 
82 /* FIX!  These are too low, but are traditional */
83 #define DEFAULT_NEWFS_BLOCK  8192U
84 #define DEFAULT_NEWFS_FRAG   1024U
85 #define DEFAULT_NEWFS_CPG    16U
86 
87 #define BIG_NEWFS_BLOCK  16384U
88 #define BIG_NEWFS_FRAG   2048U
89 #define BIG_NEWFS_CPG    64U
90 
91 static void	makelabel(const char *, struct disklabel *);
92 static int	writelabel(void);
93 static int readlabel(int flag);
94 static void	display(FILE *, const struct disklabel *);
95 static int edit(void);
96 static int	editit(void);
97 static char	*skip(char *);
98 static char	*word(char *);
99 static int	getasciilabel(FILE *, struct disklabel *);
100 static int	getasciipartspec(char *, struct disklabel *, int, int);
101 static int	checklabel(struct disklabel *);
102 static void	usage(void);
103 static struct disklabel *getvirginlabel(void);
104 
105 #define	DEFEDITOR	_PATH_VI
106 
107 static char	*dkname;
108 static char	*specname;
109 static char	tmpfil[] = PATH_TMPFILE;
110 
111 static struct	disklabel lab;
112 static u_char	bootarea[BBSIZE];
113 static off_t	mediasize;
114 static u_int	secsize;
115 static char	blank[] = "";
116 static char	unknown[] = "unknown";
117 
118 #define MAX_PART ('z')
119 #define MAX_NUM_PARTS (1 + MAX_PART - 'a')
120 static char    part_size_type[MAX_NUM_PARTS];
121 static char    part_offset_type[MAX_NUM_PARTS];
122 static int     part_set[MAX_NUM_PARTS];
123 
124 static int	installboot;	/* non-zero if we should install a boot program */
125 static int	allfields;	/* present all fields in edit */
126 static char const *xxboot;	/* primary boot */
127 
128 static int labeloffset = LABELOFFSET + LABELSECTOR * DEV_BSIZE;
129 static int bbsize = BBSIZE;
130 static int alphacksum =
131 #if defined(__alpha__)
132 	1;
133 #else
134 	0;
135 #endif
136 
137 enum	{
138 	UNSPEC, EDIT, READ, RESTORE, WRITE, WRITEBOOT
139 } op = UNSPEC;
140 
141 
142 static int	disable_write;   /* set to disable writing to disk label */
143 
144 int
145 main(int argc, char *argv[])
146 {
147 	FILE *t;
148 	int ch, error = 0;
149 	char const *name = 0;
150 
151 	while ((ch = getopt(argc, argv, "ABb:em:nRrs:w")) != -1)
152 		switch (ch) {
153 			case 'A':
154 				allfields = 1;
155 				break;
156 			case 'B':
157 				++installboot;
158 				break;
159 			case 'b':
160 				xxboot = optarg;
161 				break;
162 			case 'm':
163 				if (!strcmp(optarg, "i386")) {
164 					labeloffset = 512;
165 					bbsize = 8192;
166 					alphacksum = 0;
167 				} else if (!strcmp(optarg, "alpha")) {
168 					labeloffset = 64;
169 					bbsize = 8192;
170 					alphacksum = 1;
171 				} else {
172 					errx(1, "Unsupported architecture");
173 				}
174 				break;
175 			case 'n':
176 				disable_write = 1;
177 				break;
178 			case 'R':
179 				if (op != UNSPEC)
180 					usage();
181 				op = RESTORE;
182 				break;
183 			case 'e':
184 				if (op != UNSPEC)
185 					usage();
186 				op = EDIT;
187 				break;
188 			case 'r':
189 				/*
190 				 * We accept and ignode -r for compatibility with
191 				 * historically disklabel usage.
192 				 */
193 				break;
194 			case 'w':
195 				if (op != UNSPEC)
196 					usage();
197 				op = WRITE;
198 				break;
199 			case '?':
200 			default:
201 				usage();
202 		}
203 	argc -= optind;
204 	argv += optind;
205 
206 	if (argc < 1)
207 		usage();
208 
209 	/* Figure out the names of the thing we're working on */
210 	if (argv[0][0] != '/') {
211 		dkname = argv[0];
212 		asprintf(&specname, "%s%s", _PATH_DEV, argv[0]);
213 	} else {
214 		dkname = strrchr(argv[0], '/');
215 		dkname++;
216 		specname = argv[0];
217 	}
218 
219 	if (installboot && op == UNSPEC)
220 		op = WRITEBOOT;
221 	else if (op == UNSPEC)
222 		op = READ;
223 
224 	switch(op) {
225 
226 	case UNSPEC:
227 		break;
228 
229 	case EDIT:
230 		if (argc != 1)
231 			usage();
232 		readlabel(1);
233 		error = edit();
234 		break;
235 
236 	case READ:
237 		if (argc != 1)
238 			usage();
239 		readlabel(1);
240 		display(stdout, NULL);
241 		error = checklabel(NULL);
242 		break;
243 
244 	case RESTORE:
245 		if (argc != 2)
246 			usage();
247 		if (!(t = fopen(argv[1], "r")))
248 			err(4, "fopen %s", argv[1]);
249 		readlabel(0);
250 		if (!getasciilabel(t, &lab))
251 			exit(1);
252 		error = writelabel();
253 		break;
254 
255 	case WRITE:
256 		if (argc == 2)
257 			name = argv[1];
258 		else if (argc == 1)
259 			name = "auto";
260 		else
261 			usage();
262 		readlabel(0);
263 		makelabel(name, &lab);
264 		if (checklabel(NULL) == 0)
265 			error = writelabel();
266 		break;
267 
268 	case WRITEBOOT:
269 
270 		readlabel(1);
271 		if (argc == 2)
272 			makelabel(argv[1], &lab);
273 		if (checklabel(NULL) == 0)
274 			error = writelabel();
275 		break;
276 	}
277 	exit(error);
278 }
279 
280 /*
281  * Construct a prototype disklabel from /etc/disktab.
282  */
283 static void
284 makelabel(const char *type, struct disklabel *lp)
285 {
286 	struct disklabel *dp;
287 
288 	if (strcmp(type, "auto") == 0)
289 		dp = getvirginlabel();
290 	else
291 		dp = getdiskbyname(type);
292 	if (dp == NULL)
293 		errx(1, "%s: unknown disk type", type);
294 	*lp = *dp;
295 	bzero(lp->d_packname, sizeof(lp->d_packname));
296 }
297 
298 static void
299 readboot(void)
300 {
301 	int fd, i;
302 	struct stat st;
303 
304 	if (xxboot == NULL)
305 		xxboot = "/boot/boot";
306 	fd = open(xxboot, O_RDONLY);
307 	if (fd < 0)
308 		err(1, "cannot open %s", xxboot);
309 	fstat(fd, &st);
310 	if (st.st_size == BBSIZE) {
311 		i = read(fd, bootarea, BBSIZE);
312 		if (i != BBSIZE)
313 			err(1, "read error %s", xxboot);
314 		return;
315 	}
316 	if (alphacksum && st.st_size == BBSIZE - 512) {
317 		i = read(fd, bootarea + 512, BBSIZE - 512);
318 		if (i != BBSIZE - 512)
319 			err(1, "read error %s", xxboot);
320 		return;
321 	}
322 	errx(1, "boot code %s is wrong size", xxboot);
323 }
324 
325 static int
326 writelabel(void)
327 {
328 	uint64_t *p, sum;
329 	int i, fd;
330 	struct gctl_req *grq;
331 	char const *errstr;
332 	struct disklabel *lp = &lab;
333 
334 	if (disable_write) {
335 		warnx("write to disk label supressed - label was as follows:");
336 		display(stdout, NULL);
337 		return (0);
338 	}
339 
340 	lp->d_magic = DISKMAGIC;
341 	lp->d_magic2 = DISKMAGIC;
342 	lp->d_checksum = 0;
343 	lp->d_checksum = dkcksum(lp);
344 	if (installboot)
345 		readboot();
346 	bsd_disklabel_le_enc(bootarea + labeloffset, lp);
347 	if (alphacksum) {
348 		/* Generate the bootblock checksum for the SRM console.  */
349 		for (p = (uint64_t *)bootarea, i = 0, sum = 0; i < 63; i++)
350 			sum += p[i];
351 		p[63] = sum;
352 	}
353 
354 	fd = open(specname, O_RDWR);
355 	if (fd < 0) {
356 		grq = gctl_get_handle(GCTL_CONFIG_GEOM);
357 		gctl_ro_param(grq, "class", -1, "BSD");
358 		gctl_ro_param(grq, "geom", -1, dkname);
359 		gctl_ro_param(grq, "verb", -1, "write label");
360 		gctl_ro_param(grq, "label", 148+16*8, bootarea + labeloffset);
361 		errstr = gctl_issue(grq);
362 		if (errstr != NULL) {
363 			warnx("%s", errstr);
364 			gctl_free(grq);
365 			return(1);
366 		}
367 		gctl_free(grq);
368 		if (installboot) {
369 			grq = gctl_get_handle(GCTL_CONFIG_GEOM);
370 			gctl_ro_param(grq, "class", -1, "BSD");
371 			gctl_ro_param(grq, "geom", -1, dkname);
372 			gctl_ro_param(grq, "verb", -1, "write bootcode");
373 			gctl_ro_param(grq, "bootcode", BBSIZE, bootarea);
374 			errstr = gctl_issue(grq);
375 			if (errstr != NULL) {
376 				warnx("%s", errstr);
377 				gctl_free(grq);
378 				return (1);
379 			}
380 			gctl_free(grq);
381 		}
382 	} else {
383 		if (write(fd, bootarea, bbsize) != bbsize) {
384 			warn("write %s", specname);
385 			close (fd);
386 			return (1);
387 		}
388 		close (fd);
389 	}
390 	return (0);
391 }
392 
393 /*
394  * Fetch disklabel for disk.
395  * Use ioctl to get label unless -r flag is given.
396  */
397 static int
398 readlabel(int flag)
399 {
400 	int f;
401 	int error;
402 
403 	f = open(specname, O_RDONLY);
404 	if (f < 0)
405 		err(1, specname);
406 	(void)lseek(f, (off_t)0, SEEK_SET);
407 	if (read(f, bootarea, BBSIZE) != BBSIZE)
408 		err(4, "%s read", specname);
409 	close (f);
410 	error = bsd_disklabel_le_dec(bootarea + labeloffset, &lab, MAXPARTITIONS);
411 	if (flag && error)
412 		errx(1, "%s: no valid label found", specname);
413 	return (error);
414 }
415 
416 
417 static void
418 display(FILE *f, const struct disklabel *lp)
419 {
420 	int i, j;
421 	const struct partition *pp;
422 
423 	if (lp == NULL)
424 		lp = &lab;
425 
426 	fprintf(f, "# %s:\n", specname);
427 	if (allfields) {
428 		if (lp->d_type < DKMAXTYPES)
429 			fprintf(f, "type: %s\n", dktypenames[lp->d_type]);
430 		else
431 			fprintf(f, "type: %u\n", lp->d_type);
432 		fprintf(f, "disk: %.*s\n", (int)sizeof(lp->d_typename),
433 			lp->d_typename);
434 		fprintf(f, "label: %.*s\n", (int)sizeof(lp->d_packname),
435 			lp->d_packname);
436 		fprintf(f, "flags:");
437 		if (lp->d_flags & D_REMOVABLE)
438 			fprintf(f, " removeable");
439 		if (lp->d_flags & D_ECC)
440 			fprintf(f, " ecc");
441 		if (lp->d_flags & D_BADSECT)
442 			fprintf(f, " badsect");
443 		fprintf(f, "\n");
444 		fprintf(f, "bytes/sector: %lu\n", (u_long)lp->d_secsize);
445 		fprintf(f, "sectors/track: %lu\n", (u_long)lp->d_nsectors);
446 		fprintf(f, "tracks/cylinder: %lu\n", (u_long)lp->d_ntracks);
447 		fprintf(f, "sectors/cylinder: %lu\n", (u_long)lp->d_secpercyl);
448 		fprintf(f, "cylinders: %lu\n", (u_long)lp->d_ncylinders);
449 		fprintf(f, "sectors/unit: %lu\n", (u_long)lp->d_secperunit);
450 		fprintf(f, "rpm: %u\n", lp->d_rpm);
451 		fprintf(f, "interleave: %u\n", lp->d_interleave);
452 		fprintf(f, "trackskew: %u\n", lp->d_trackskew);
453 		fprintf(f, "cylinderskew: %u\n", lp->d_cylskew);
454 		fprintf(f, "headswitch: %lu\t\t# milliseconds\n",
455 		    (u_long)lp->d_headswitch);
456 		fprintf(f, "track-to-track seek: %ld\t# milliseconds\n",
457 		    (u_long)lp->d_trkseek);
458 		fprintf(f, "drivedata: ");
459 		for (i = NDDATA - 1; i >= 0; i--)
460 			if (lp->d_drivedata[i])
461 				break;
462 		if (i < 0)
463 			i = 0;
464 		for (j = 0; j <= i; j++)
465 			fprintf(f, "%lu ", (u_long)lp->d_drivedata[j]);
466 		fprintf(f, "\n\n");
467 	}
468 	fprintf(f, "%u partitions:\n", lp->d_npartitions);
469 	fprintf(f,
470 	    "#        size   offset    fstype   [fsize bsize bps/cpg]\n");
471 	pp = lp->d_partitions;
472 	for (i = 0; i < lp->d_npartitions; i++, pp++) {
473 		if (pp->p_size) {
474 			fprintf(f, "  %c: %8lu %8lu  ", 'a' + i,
475 			   (u_long)pp->p_size, (u_long)pp->p_offset);
476 			if (pp->p_fstype < FSMAXTYPES)
477 				fprintf(f, "%8.8s", fstypenames[pp->p_fstype]);
478 			else
479 				fprintf(f, "%8d", pp->p_fstype);
480 			switch (pp->p_fstype) {
481 
482 			case FS_UNUSED:				/* XXX */
483 				fprintf(f, "    %5lu %5lu %5.5s ",
484 				    (u_long)pp->p_fsize,
485 				    (u_long)(pp->p_fsize * pp->p_frag), "");
486 				break;
487 
488 			case FS_BSDFFS:
489 				fprintf(f, "    %5lu %5lu %5u ",
490 				    (u_long)pp->p_fsize,
491 				    (u_long)(pp->p_fsize * pp->p_frag),
492 				    pp->p_cpg);
493 				break;
494 
495 			case FS_BSDLFS:
496 				fprintf(f, "    %5lu %5lu %5d",
497 				    (u_long)pp->p_fsize,
498 				    (u_long)(pp->p_fsize * pp->p_frag),
499 				    pp->p_cpg);
500 				break;
501 
502 			default:
503 				fprintf(f, "%20.20s", "");
504 				break;
505 			}
506 			if (i == RAW_PART) {
507 				fprintf(f, "  # \"raw\" part, don't edit");
508 			}
509 			fprintf(f, "\n");
510 		}
511 	}
512 	fflush(f);
513 }
514 
515 static int
516 edit(void)
517 {
518 	int c, fd;
519 	struct disklabel label;
520 	FILE *fp;
521 
522 	if ((fd = mkstemp(tmpfil)) == -1 ||
523 	    (fp = fdopen(fd, "w")) == NULL) {
524 		warnx("can't create %s", tmpfil);
525 		return (1);
526 	}
527 	display(fp, NULL);
528 	fclose(fp);
529 	for (;;) {
530 		if (!editit())
531 			break;
532 		fp = fopen(tmpfil, "r");
533 		if (fp == NULL) {
534 			warnx("can't reopen %s for reading", tmpfil);
535 			break;
536 		}
537 		bzero((char *)&label, sizeof(label));
538 		c = getasciilabel(fp, &label);
539 		fclose(fp);
540 		if (c) {
541 			lab = label;
542 			if (writelabel() == 0) {
543 				(void) unlink(tmpfil);
544 				return (0);
545 			}
546 		}
547 		printf("re-edit the label? [y]: ");
548 		fflush(stdout);
549 		c = getchar();
550 		if (c != EOF && c != (int)'\n')
551 			while (getchar() != (int)'\n')
552 				;
553 		if  (c == (int)'n')
554 			break;
555 	}
556 	(void) unlink(tmpfil);
557 	return (1);
558 }
559 
560 static int
561 editit(void)
562 {
563 	int pid, xpid;
564 	int locstat, omask;
565 	const char *ed;
566 
567 	omask = sigblock(sigmask(SIGINT)|sigmask(SIGQUIT)|sigmask(SIGHUP));
568 	while ((pid = fork()) < 0) {
569 		if (errno == EPROCLIM) {
570 			warnx("you have too many processes");
571 			return(0);
572 		}
573 		if (errno != EAGAIN) {
574 			warn("fork");
575 			return(0);
576 		}
577 		sleep(1);
578 	}
579 	if (pid == 0) {
580 		sigsetmask(omask);
581 		setgid(getgid());
582 		setuid(getuid());
583 		if ((ed = getenv("EDITOR")) == (char *)0)
584 			ed = DEFEDITOR;
585 		execlp(ed, ed, tmpfil, (char *)0);
586 		err(1, "%s", ed);
587 	}
588 	while ((xpid = wait(&locstat)) >= 0)
589 		if (xpid == pid)
590 			break;
591 	sigsetmask(omask);
592 	return(!locstat);
593 }
594 
595 static char *
596 skip(char *cp)
597 {
598 
599 	while (*cp != '\0' && isspace(*cp))
600 		cp++;
601 	if (*cp == '\0' || *cp == '#')
602 		return (NULL);
603 	return (cp);
604 }
605 
606 static char *
607 word(char *cp)
608 {
609 	char c;
610 
611 	while (*cp != '\0' && !isspace(*cp) && *cp != '#')
612 		cp++;
613 	if ((c = *cp) != '\0') {
614 		*cp++ = '\0';
615 		if (c != '#')
616 			return (skip(cp));
617 	}
618 	return (NULL);
619 }
620 
621 /*
622  * Read an ascii label in from fd f,
623  * in the same format as that put out by display(),
624  * and fill in lp.
625  */
626 static int
627 getasciilabel(FILE *f, struct disklabel *lp)
628 {
629 	char *cp;
630 	const char **cpp;
631 	u_int part;
632 	char *tp, line[BUFSIZ];
633 	u_long v;
634 	int lineno = 0, errors = 0;
635 	int i;
636 
637 	bzero(&part_set, sizeof(part_set));
638 	bzero(&part_size_type, sizeof(part_size_type));
639 	bzero(&part_offset_type, sizeof(part_offset_type));
640 	lp->d_bbsize = BBSIZE;				/* XXX */
641 	lp->d_sbsize = 0;				/* XXX */
642 	while (fgets(line, sizeof(line) - 1, f)) {
643 		lineno++;
644 		if ((cp = index(line,'\n')) != 0)
645 			*cp = '\0';
646 		cp = skip(line);
647 		if (cp == NULL)
648 			continue;
649 		tp = index(cp, ':');
650 		if (tp == NULL) {
651 			fprintf(stderr, "line %d: syntax error\n", lineno);
652 			errors++;
653 			continue;
654 		}
655 		*tp++ = '\0', tp = skip(tp);
656 		if (!strcmp(cp, "type")) {
657 			if (tp == NULL)
658 				tp = unknown;
659 			cpp = dktypenames;
660 			for (; cpp < &dktypenames[DKMAXTYPES]; cpp++)
661 				if (*cpp && !strcmp(*cpp, tp)) {
662 					lp->d_type = cpp - dktypenames;
663 					break;
664 				}
665 			if (cpp < &dktypenames[DKMAXTYPES])
666 				continue;
667 			v = strtoul(tp, NULL, 10);
668 			if (v >= DKMAXTYPES)
669 				fprintf(stderr, "line %d:%s %lu\n", lineno,
670 				    "Warning, unknown disk type", v);
671 			lp->d_type = v;
672 			continue;
673 		}
674 		if (!strcmp(cp, "flags")) {
675 			for (v = 0; (cp = tp) && *cp != '\0';) {
676 				tp = word(cp);
677 				if (!strcmp(cp, "removeable"))
678 					v |= D_REMOVABLE;
679 				else if (!strcmp(cp, "ecc"))
680 					v |= D_ECC;
681 				else if (!strcmp(cp, "badsect"))
682 					v |= D_BADSECT;
683 				else {
684 					fprintf(stderr,
685 					    "line %d: %s: bad flag\n",
686 					    lineno, cp);
687 					errors++;
688 				}
689 			}
690 			lp->d_flags = v;
691 			continue;
692 		}
693 		if (!strcmp(cp, "drivedata")) {
694 			for (i = 0; (cp = tp) && *cp != '\0' && i < NDDATA;) {
695 				lp->d_drivedata[i++] = strtoul(cp, NULL, 10);
696 				tp = word(cp);
697 			}
698 			continue;
699 		}
700 		if (sscanf(cp, "%lu partitions", &v) == 1) {
701 			if (v == 0 || v > MAXPARTITIONS) {
702 				fprintf(stderr,
703 				    "line %d: bad # of partitions\n", lineno);
704 				lp->d_npartitions = MAXPARTITIONS;
705 				errors++;
706 			} else
707 				lp->d_npartitions = v;
708 			continue;
709 		}
710 		if (tp == NULL)
711 			tp = blank;
712 		if (!strcmp(cp, "disk")) {
713 			strncpy(lp->d_typename, tp, sizeof (lp->d_typename));
714 			continue;
715 		}
716 		if (!strcmp(cp, "label")) {
717 			strncpy(lp->d_packname, tp, sizeof (lp->d_packname));
718 			continue;
719 		}
720 		if (!strcmp(cp, "bytes/sector")) {
721 			v = strtoul(tp, NULL, 10);
722 			if (v == 0 || (v % DEV_BSIZE) != 0) {
723 				fprintf(stderr,
724 				    "line %d: %s: bad sector size\n",
725 				    lineno, tp);
726 				errors++;
727 			} else
728 				lp->d_secsize = v;
729 			continue;
730 		}
731 		if (!strcmp(cp, "sectors/track")) {
732 			v = strtoul(tp, NULL, 10);
733 #if (ULONG_MAX != 0xffffffffUL)
734 			if (v == 0 || v > 0xffffffff) {
735 #else
736 			if (v == 0) {
737 #endif
738 				fprintf(stderr, "line %d: %s: bad %s\n",
739 				    lineno, tp, cp);
740 				errors++;
741 			} else
742 				lp->d_nsectors = v;
743 			continue;
744 		}
745 		if (!strcmp(cp, "sectors/cylinder")) {
746 			v = strtoul(tp, NULL, 10);
747 			if (v == 0) {
748 				fprintf(stderr, "line %d: %s: bad %s\n",
749 				    lineno, tp, cp);
750 				errors++;
751 			} else
752 				lp->d_secpercyl = v;
753 			continue;
754 		}
755 		if (!strcmp(cp, "tracks/cylinder")) {
756 			v = strtoul(tp, NULL, 10);
757 			if (v == 0) {
758 				fprintf(stderr, "line %d: %s: bad %s\n",
759 				    lineno, tp, cp);
760 				errors++;
761 			} else
762 				lp->d_ntracks = v;
763 			continue;
764 		}
765 		if (!strcmp(cp, "cylinders")) {
766 			v = strtoul(tp, NULL, 10);
767 			if (v == 0) {
768 				fprintf(stderr, "line %d: %s: bad %s\n",
769 				    lineno, tp, cp);
770 				errors++;
771 			} else
772 				lp->d_ncylinders = v;
773 			continue;
774 		}
775 		if (!strcmp(cp, "sectors/unit")) {
776 			v = strtoul(tp, NULL, 10);
777 			if (v == 0) {
778 				fprintf(stderr, "line %d: %s: bad %s\n",
779 				    lineno, tp, cp);
780 				errors++;
781 			} else
782 				lp->d_secperunit = v;
783 			continue;
784 		}
785 		if (!strcmp(cp, "rpm")) {
786 			v = strtoul(tp, NULL, 10);
787 			if (v == 0 || v > USHRT_MAX) {
788 				fprintf(stderr, "line %d: %s: bad %s\n",
789 				    lineno, tp, cp);
790 				errors++;
791 			} else
792 				lp->d_rpm = v;
793 			continue;
794 		}
795 		if (!strcmp(cp, "interleave")) {
796 			v = strtoul(tp, NULL, 10);
797 			if (v == 0 || v > USHRT_MAX) {
798 				fprintf(stderr, "line %d: %s: bad %s\n",
799 				    lineno, tp, cp);
800 				errors++;
801 			} else
802 				lp->d_interleave = v;
803 			continue;
804 		}
805 		if (!strcmp(cp, "trackskew")) {
806 			v = strtoul(tp, NULL, 10);
807 			if (v > USHRT_MAX) {
808 				fprintf(stderr, "line %d: %s: bad %s\n",
809 				    lineno, tp, cp);
810 				errors++;
811 			} else
812 				lp->d_trackskew = v;
813 			continue;
814 		}
815 		if (!strcmp(cp, "cylinderskew")) {
816 			v = strtoul(tp, NULL, 10);
817 			if (v > USHRT_MAX) {
818 				fprintf(stderr, "line %d: %s: bad %s\n",
819 				    lineno, tp, cp);
820 				errors++;
821 			} else
822 				lp->d_cylskew = v;
823 			continue;
824 		}
825 		if (!strcmp(cp, "headswitch")) {
826 			v = strtoul(tp, NULL, 10);
827 			lp->d_headswitch = v;
828 			continue;
829 		}
830 		if (!strcmp(cp, "track-to-track seek")) {
831 			v = strtoul(tp, NULL, 10);
832 			lp->d_trkseek = v;
833 			continue;
834 		}
835 		/* the ':' was removed above */
836 		if (*cp < 'a' || *cp > MAX_PART || cp[1] != '\0') {
837 			fprintf(stderr,
838 			    "line %d: %s: Unknown disklabel field\n", lineno,
839 			    cp);
840 			errors++;
841 			continue;
842 		}
843 
844 		/* Process a partition specification line. */
845 		part = *cp - 'a';
846 		if (part >= lp->d_npartitions) {
847 			fprintf(stderr,
848 			    "line %d: partition name out of range a-%c: %s\n",
849 			    lineno, 'a' + lp->d_npartitions - 1, cp);
850 			errors++;
851 			continue;
852 		}
853 		part_set[part] = 1;
854 
855 		if (getasciipartspec(tp, lp, part, lineno) != 0) {
856 			errors++;
857 			break;
858 		}
859 	}
860 	errors += checklabel(lp);
861 	return (errors == 0);
862 }
863 
864 #define NXTNUM(n) do { \
865 	if (tp == NULL) { \
866 		fprintf(stderr, "line %d: too few numeric fields\n", lineno); \
867 		return (1); \
868 	} else { \
869 		cp = tp, tp = word(cp); \
870 		(n) = strtoul(cp, NULL, 10); \
871 	} \
872 } while (0)
873 
874 /* retain 1 character following number */
875 #define NXTWORD(w,n) do { \
876 	if (tp == NULL) { \
877 		fprintf(stderr, "line %d: too few numeric fields\n", lineno); \
878 		return (1); \
879 	} else { \
880 	        char *tmp; \
881 		cp = tp, tp = word(cp); \
882 	        (n) = strtoul(cp, &tmp, 10); \
883 		if (tmp) (w) = *tmp; \
884 	} \
885 } while (0)
886 
887 /*
888  * Read a partition line into partition `part' in the specified disklabel.
889  * Return 0 on success, 1 on failure.
890  */
891 static int
892 getasciipartspec(char *tp, struct disklabel *lp, int part, int lineno)
893 {
894 	struct partition *pp;
895 	char *cp;
896 	const char **cpp;
897 	u_long v;
898 
899 	pp = &lp->d_partitions[part];
900 	cp = NULL;
901 
902 	v = 0;
903 	NXTWORD(part_size_type[part],v);
904 	if (v == 0 && part_size_type[part] != '*') {
905 		fprintf(stderr,
906 		    "line %d: %s: bad partition size\n", lineno, cp);
907 		return (1);
908 	}
909 	pp->p_size = v;
910 
911 	v = 0;
912 	NXTWORD(part_offset_type[part],v);
913 	if (v == 0 && part_offset_type[part] != '*' &&
914 	    part_offset_type[part] != '\0') {
915 		fprintf(stderr,
916 		    "line %d: %s: bad partition offset\n", lineno, cp);
917 		return (1);
918 	}
919 	pp->p_offset = v;
920 	if (tp == NULL) {
921 		fprintf(stderr, "line %d: missing file system type\n", lineno);
922 		return (1);
923 	}
924 	cp = tp, tp = word(cp);
925 	for (cpp = fstypenames; cpp < &fstypenames[FSMAXTYPES]; cpp++)
926 		if (*cpp && !strcmp(*cpp, cp))
927 			break;
928 	if (*cpp != NULL) {
929 		pp->p_fstype = cpp - fstypenames;
930 	} else {
931 		if (isdigit(*cp))
932 			v = strtoul(cp, NULL, 10);
933 		else
934 			v = FSMAXTYPES;
935 		if (v >= FSMAXTYPES) {
936 			fprintf(stderr,
937 			    "line %d: Warning, unknown file system type %s\n",
938 			    lineno, cp);
939 			v = FS_UNUSED;
940 		}
941 		pp->p_fstype = v;
942 	}
943 
944 	switch (pp->p_fstype) {
945 	case FS_UNUSED:
946 		/*
947 		 * allow us to accept defaults for
948 		 * fsize/frag/cpg
949 		 */
950 		if (tp) {
951 			NXTNUM(pp->p_fsize);
952 			if (pp->p_fsize == 0)
953 				break;
954 			NXTNUM(v);
955 			pp->p_frag = v / pp->p_fsize;
956 		}
957 		/* else default to 0's */
958 		break;
959 
960 	/* These happen to be the same */
961 	case FS_BSDFFS:
962 	case FS_BSDLFS:
963 		if (tp) {
964 			NXTNUM(pp->p_fsize);
965 			if (pp->p_fsize == 0)
966 				break;
967 			NXTNUM(v);
968 			pp->p_frag = v / pp->p_fsize;
969 			NXTNUM(pp->p_cpg);
970 		} else {
971 			/*
972 			 * FIX! poor attempt at adaptive
973 			 */
974 			/* 1 GB */
975 			if (pp->p_size < 1024*1024*1024 / lp->d_secsize) {
976 				/*
977 				 * FIX! These are too low, but are traditional
978 				 */
979 				pp->p_fsize = DEFAULT_NEWFS_FRAG;
980 				pp->p_frag = DEFAULT_NEWFS_BLOCK /
981 				    DEFAULT_NEWFS_FRAG;
982 				pp->p_cpg = DEFAULT_NEWFS_CPG;
983 			} else {
984 				pp->p_fsize = BIG_NEWFS_FRAG;
985 				pp->p_frag = BIG_NEWFS_BLOCK /
986 				    BIG_NEWFS_FRAG;
987 				pp->p_cpg = BIG_NEWFS_CPG;
988 			}
989 		}
990 	default:
991 		break;
992 	}
993 	return (0);
994 }
995 
996 /*
997  * Check disklabel for errors and fill in
998  * derived fields according to supplied values.
999  */
1000 static int
1001 checklabel(struct disklabel *lp)
1002 {
1003 	struct partition *pp;
1004 	int i, errors = 0;
1005 	char part;
1006 	u_long total_size, total_percent, current_offset;
1007 	int seen_default_offset;
1008 	int hog_part;
1009 	int j;
1010 	struct partition *pp2;
1011 
1012 	if (lp == NULL)
1013 		lp = &lab;
1014 
1015 	if (allfields) {
1016 
1017 		if (lp->d_secsize == 0) {
1018 			fprintf(stderr, "sector size 0\n");
1019 			return (1);
1020 		}
1021 		if (lp->d_nsectors == 0) {
1022 			fprintf(stderr, "sectors/track 0\n");
1023 			return (1);
1024 		}
1025 		if (lp->d_ntracks == 0) {
1026 			fprintf(stderr, "tracks/cylinder 0\n");
1027 			return (1);
1028 		}
1029 		if  (lp->d_ncylinders == 0) {
1030 			fprintf(stderr, "cylinders/unit 0\n");
1031 			errors++;
1032 		}
1033 		if (lp->d_rpm == 0)
1034 			warnx("revolutions/minute 0");
1035 		if (lp->d_secpercyl == 0)
1036 			lp->d_secpercyl = lp->d_nsectors * lp->d_ntracks;
1037 		if (lp->d_secperunit == 0)
1038 			lp->d_secperunit = lp->d_secpercyl * lp->d_ncylinders;
1039 		if (lp->d_bbsize == 0) {
1040 			fprintf(stderr, "boot block size 0\n");
1041 			errors++;
1042 		} else if (lp->d_bbsize % lp->d_secsize)
1043 			warnx("boot block size %% sector-size != 0");
1044 		if (lp->d_npartitions > MAXPARTITIONS)
1045 			warnx("number of partitions (%lu) > MAXPARTITIONS (%d)",
1046 			    (u_long)lp->d_npartitions, MAXPARTITIONS);
1047 	} else {
1048 		struct disklabel *vl;
1049 
1050 		vl = getvirginlabel();
1051 		lp->d_secsize = vl->d_secsize;
1052 		lp->d_nsectors = vl->d_nsectors;
1053 		lp->d_ntracks = vl->d_ntracks;
1054 		lp->d_ncylinders = vl->d_ncylinders;
1055 		lp->d_rpm = vl->d_rpm;
1056 		lp->d_interleave = vl->d_interleave;
1057 		lp->d_secpercyl = vl->d_secpercyl;
1058 		lp->d_secperunit = vl->d_secperunit;
1059 		lp->d_bbsize = vl->d_bbsize;
1060 		lp->d_npartitions = vl->d_npartitions;
1061 	}
1062 
1063 
1064 	/* first allocate space to the partitions, then offsets */
1065 	total_size = 0; /* in sectors */
1066 	total_percent = 0; /* in percent */
1067 	hog_part = -1;
1068 	/* find all fixed partitions */
1069 	for (i = 0; i < lp->d_npartitions; i++) {
1070 		pp = &lp->d_partitions[i];
1071 		if (part_set[i]) {
1072 			if (part_size_type[i] == '*') {
1073 				if (i == RAW_PART) {
1074 					pp->p_size = lp->d_secperunit;
1075 				} else {
1076 					if (hog_part != -1)
1077 						warnx("Too many '*' partitions (%c and %c)",
1078 						    hog_part + 'a',i + 'a');
1079 					else
1080 						hog_part = i;
1081 				}
1082 			} else {
1083 				off_t size;
1084 
1085 				size = pp->p_size;
1086 				switch (part_size_type[i]) {
1087 				case '%':
1088 					total_percent += size;
1089 					break;
1090 				case 'k':
1091 				case 'K':
1092 					size *= 1024ULL;
1093 					break;
1094 				case 'm':
1095 				case 'M':
1096 					size *= 1024ULL * 1024ULL;
1097 					break;
1098 				case 'g':
1099 				case 'G':
1100 					size *= 1024ULL * 1024ULL * 1024ULL;
1101 					break;
1102 				case '\0':
1103 					break;
1104 				default:
1105 					warnx("unknown size specifier '%c' (K/M/G are valid)",part_size_type[i]);
1106 					break;
1107 				}
1108 				/* don't count %'s yet */
1109 				if (part_size_type[i] != '%') {
1110 					/*
1111 					 * for all not in sectors, convert to
1112 					 * sectors
1113 					 */
1114 					if (part_size_type[i] != '\0') {
1115 						if (size % lp->d_secsize != 0)
1116 							warnx("partition %c not an integer number of sectors",
1117 							    i + 'a');
1118 						size /= lp->d_secsize;
1119 						pp->p_size = size;
1120 					}
1121 					/* else already in sectors */
1122 					if (i != RAW_PART)
1123 						total_size += size;
1124 				}
1125 			}
1126 		}
1127 	}
1128 	/* handle % partitions - note %'s don't need to add up to 100! */
1129 	if (total_percent != 0) {
1130 		long free_space = lp->d_secperunit - total_size;
1131 		if (total_percent > 100) {
1132 			fprintf(stderr,"total percentage %lu is greater than 100\n",
1133 			    total_percent);
1134 			errors++;
1135 		}
1136 
1137 		if (free_space > 0) {
1138 			for (i = 0; i < lp->d_npartitions; i++) {
1139 				pp = &lp->d_partitions[i];
1140 				if (part_set[i] && part_size_type[i] == '%') {
1141 					/* careful of overflows! and integer roundoff */
1142 					pp->p_size = ((double)pp->p_size/100) * free_space;
1143 					total_size += pp->p_size;
1144 
1145 					/* FIX we can lose a sector or so due to roundoff per
1146 					   partition.  A more complex algorithm could avoid that */
1147 				}
1148 			}
1149 		} else {
1150 			fprintf(stderr,
1151 			    "%ld sectors available to give to '*' and '%%' partitions\n",
1152 			    free_space);
1153 			errors++;
1154 			/* fix?  set all % partitions to size 0? */
1155 		}
1156 	}
1157 	/* give anything remaining to the hog partition */
1158 	if (hog_part != -1) {
1159 		lp->d_partitions[hog_part].p_size = lp->d_secperunit - total_size;
1160 		total_size = lp->d_secperunit;
1161 	}
1162 
1163 	/* Now set the offsets for each partition */
1164 	current_offset = 0; /* in sectors */
1165 	seen_default_offset = 0;
1166 	for (i = 0; i < lp->d_npartitions; i++) {
1167 		part = 'a' + i;
1168 		pp = &lp->d_partitions[i];
1169 		if (part_set[i]) {
1170 			if (part_offset_type[i] == '*') {
1171 				if (i == RAW_PART) {
1172 					pp->p_offset = 0;
1173 				} else {
1174 					pp->p_offset = current_offset;
1175 					seen_default_offset = 1;
1176 				}
1177 			} else {
1178 				/* allow them to be out of order for old-style tables */
1179 				if (pp->p_offset < current_offset &&
1180 				    seen_default_offset && i != RAW_PART &&
1181 				    pp->p_fstype != FS_VINUM) {
1182 					fprintf(stderr,
1183 "Offset %ld for partition %c overlaps previous partition which ends at %lu\n",
1184 					    (long)pp->p_offset,i+'a',current_offset);
1185 					fprintf(stderr,
1186 "Labels with any *'s for offset must be in ascending order by sector\n");
1187 					errors++;
1188 				} else if (pp->p_offset != current_offset &&
1189 				    i != RAW_PART && seen_default_offset) {
1190 					/*
1191 					 * this may give unneeded warnings if
1192 					 * partitions are out-of-order
1193 					 */
1194 					warnx(
1195 "Offset %ld for partition %c doesn't match expected value %ld",
1196 					    (long)pp->p_offset, i + 'a', current_offset);
1197 				}
1198 			}
1199 			if (i != RAW_PART)
1200 				current_offset = pp->p_offset + pp->p_size;
1201 		}
1202 	}
1203 
1204 	for (i = 0; i < lp->d_npartitions; i++) {
1205 		part = 'a' + i;
1206 		pp = &lp->d_partitions[i];
1207 		if (pp->p_size == 0 && pp->p_offset != 0)
1208 			warnx("partition %c: size 0, but offset %lu",
1209 			    part, (u_long)pp->p_offset);
1210 #ifdef notdef
1211 		if (pp->p_size % lp->d_secpercyl)
1212 			warnx("partition %c: size %% cylinder-size != 0",
1213 			    part);
1214 		if (pp->p_offset % lp->d_secpercyl)
1215 			warnx("partition %c: offset %% cylinder-size != 0",
1216 			    part);
1217 #endif
1218 		if (pp->p_offset > lp->d_secperunit) {
1219 			fprintf(stderr,
1220 			    "partition %c: offset past end of unit\n", part);
1221 			errors++;
1222 		}
1223 		if (pp->p_offset + pp->p_size > lp->d_secperunit) {
1224 			fprintf(stderr,
1225 			"partition %c: partition extends past end of unit\n",
1226 			    part);
1227 			errors++;
1228 		}
1229 		if (i == RAW_PART) {
1230 			if (pp->p_fstype != FS_UNUSED)
1231 				warnx("partition %c is not marked as unused!",part);
1232 			if (pp->p_offset != 0)
1233 				warnx("partition %c doesn't start at 0!",part);
1234 			if (pp->p_size != lp->d_secperunit)
1235 				warnx("partition %c doesn't cover the whole unit!",part);
1236 
1237 			if ((pp->p_fstype != FS_UNUSED) || (pp->p_offset != 0) ||
1238 			    (pp->p_size != lp->d_secperunit)) {
1239 				warnx("An incorrect partition %c may cause problems for "
1240 				    "standard system utilities",part);
1241 			}
1242 		}
1243 
1244 		/* check for overlaps */
1245 		/* this will check for all possible overlaps once and only once */
1246 		for (j = 0; j < i; j++) {
1247 			pp2 = &lp->d_partitions[j];
1248 			if (j != RAW_PART && i != RAW_PART &&
1249 			    pp->p_fstype != FS_VINUM &&
1250 			    pp2->p_fstype != FS_VINUM &&
1251 			    part_set[i] && part_set[j]) {
1252 				if (pp2->p_offset < pp->p_offset + pp->p_size &&
1253 				    (pp2->p_offset + pp2->p_size > pp->p_offset ||
1254 					pp2->p_offset >= pp->p_offset)) {
1255 					fprintf(stderr,"partitions %c and %c overlap!\n",
1256 					    j + 'a', i + 'a');
1257 					errors++;
1258 				}
1259 			}
1260 		}
1261 	}
1262 	for (; i < MAXPARTITIONS; i++) {
1263 		part = 'a' + i;
1264 		pp = &lp->d_partitions[i];
1265 		if (pp->p_size || pp->p_offset)
1266 			warnx("unused partition %c: size %d offset %lu",
1267 			    'a' + i, pp->p_size, (u_long)pp->p_offset);
1268 	}
1269 	return (errors);
1270 }
1271 
1272 /*
1273  * When operating on a "virgin" disk, try getting an initial label
1274  * from the associated device driver.  This might work for all device
1275  * drivers that are able to fetch some initial device parameters
1276  * without even having access to a (BSD) disklabel, like SCSI disks,
1277  * most IDE drives, or vn devices.
1278  *
1279  * The device name must be given in its "canonical" form.
1280  */
1281 static struct disklabel *
1282 getvirginlabel(void)
1283 {
1284 	static struct disklabel loclab;
1285 	struct partition *dp;
1286 	int f;
1287 	u_int u;
1288 
1289 	if ((f = open(specname, O_RDONLY)) == -1) {
1290 		warn("cannot open %s", specname);
1291 		return (NULL);
1292 	}
1293 
1294 	/* New world order */
1295 	if ((ioctl(f, DIOCGMEDIASIZE, &mediasize) != 0) ||
1296 	    (ioctl(f, DIOCGSECTORSIZE, &secsize) != 0)) {
1297 		close (f);
1298 		return (NULL);
1299 	}
1300 	memset(&loclab, 0, sizeof loclab);
1301 	loclab.d_magic = DISKMAGIC;
1302 	loclab.d_magic2 = DISKMAGIC;
1303 	loclab.d_secsize = secsize;
1304 	loclab.d_secperunit = mediasize / secsize;
1305 
1306 	/*
1307 	 * Nobody in these enligthened days uses the CHS geometry for
1308 	 * anything, but nontheless try to get it right.  If we fail
1309 	 * to get any good ideas from the device, construct something
1310 	 * which is IBM-PC friendly.
1311 	 */
1312 	if (ioctl(f, DIOCGFWSECTORS, &u) == 0)
1313 		loclab.d_nsectors = u;
1314 	else
1315 		loclab.d_nsectors = 63;
1316 	if (ioctl(f, DIOCGFWHEADS, &u) == 0)
1317 		loclab.d_ntracks = u;
1318 	else if (loclab.d_secperunit <= 63*1*1024)
1319 		loclab.d_ntracks = 1;
1320 	else if (loclab.d_secperunit <= 63*16*1024)
1321 		loclab.d_ntracks = 16;
1322 	else
1323 		loclab.d_ntracks = 255;
1324 	loclab.d_secpercyl = loclab.d_ntracks * loclab.d_nsectors;
1325 	loclab.d_ncylinders = loclab.d_secperunit / loclab.d_secpercyl;
1326 	loclab.d_npartitions = MAXPARTITIONS;
1327 
1328 	/* Various (unneeded) compat stuff */
1329 	loclab.d_rpm = 3600;
1330 	loclab.d_bbsize = BBSIZE;
1331 	loclab.d_interleave = 1;
1332 	strncpy(loclab.d_typename, "amnesiac",
1333 	    sizeof(loclab.d_typename));
1334 
1335 	dp = &loclab.d_partitions[RAW_PART];
1336 	dp->p_size = loclab.d_secperunit;
1337 	loclab.d_checksum = dkcksum(&loclab);
1338 	close (f);
1339 	return (&loclab);
1340 }
1341 
1342 static void
1343 usage(void)
1344 {
1345 
1346 	fprintf(stderr,
1347 	"%s\n%s\n%s\n%s\n%s\n%s\n%s\n%s\n%s\n%s\n%s\n%s\n%s\n%s\n",
1348 	"usage: bsdlabel disk",
1349 	"\t\t(to read label)",
1350 	"	bsdlabel -w [-n] [-m machine] disk [type]",
1351 	"\t\t(to write label with existing boot program)",
1352 	"	bsdlabel -e [-n] [-m machine] disk",
1353 	"\t\t(to edit label)",
1354 	"	bsdlabel -R [-n] [-m machine] disk protofile",
1355 	"\t\t(to restore label with existing boot program)",
1356 	"	bsdlabel -B [-b boot] [-m machine] disk",
1357 	"\t\t(to install boot program with existing on-disk label)",
1358 	"	bsdlabel -w -B [-n] [-b boot] [-m machine] disk [type]",
1359 	"\t\t(to write label and install boot program)",
1360 	"	bsdlabel -R -B [-n] [-b boot] [-m machine] disk protofile",
1361 		"\t\t(to restore label and install boot program)"
1362 	);
1363 	exit(1);
1364 }
1365