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