bsdlabel.c revision 114557
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 114557 2003-05-02 22:46:44Z 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	    MAXPARTITIONS);
418	return (&lab);
419}
420
421/*
422 * Construct a bootarea (bbsize bytes) in the specified buffer ``boot''
423 * Returns a pointer to the disklabel portion of the bootarea.
424 */
425struct disklabel *
426makebootarea(void *boot, struct disklabel *dp, int f)
427{
428	struct disklabel *lp;
429	char *p;
430	int b;
431	char *dkbasename;
432	struct stat sb;
433	uint64_t *bootinfo;
434	int n;
435
436	/* XXX */
437	if (dp->d_secsize == 0) {
438		dp->d_secsize = DEV_BSIZE;
439		dp->d_bbsize = bbsize;
440	}
441	lp = (struct disklabel *)((char *)boot + labeloffset);
442	bzero((char *)lp, sizeof *lp);
443	/*
444	 * If we are not installing a boot program but we are installing a
445	 * label on disk then we must read the current bootarea so we don't
446	 * clobber the existing boot.
447	 */
448	if (!installboot) {
449		if (read(f, boot, BBSIZE) < BBSIZE)
450			err(4, "%s", specname);
451		bzero((char *)lp, sizeof *lp);
452		return (lp);
453	}
454	/*
455	 * We are installing a boot program.  Determine the name(s) and
456	 * read them into the appropriate places in the boot area.
457	 */
458	if (!xxboot) {
459		dkbasename = np;
460		if ((p = rindex(dkname, '/')) == NULL)
461			p = dkname;
462		else
463			p++;
464		while (*p && !isdigit(*p))
465			*np++ = *p++;
466		*np++ = '\0';
467
468		if (!xxboot) {
469			(void)sprintf(boot0, "%s/boot", _PATH_BOOTDIR);
470			xxboot = boot0;
471		}
472	}
473
474	b = open(xxboot, O_RDONLY);
475	if (b < 0)
476		err(4, "%s", xxboot);
477	if (fstat(b, &sb) != 0)
478		err(4, "%s", xxboot);
479
480	if (alphacksum) {
481		if (sb.st_size > BBSIZE - dp->d_secsize)
482			errx(4, "%s too large", xxboot);
483		/*
484		 * On the alpha, the primary bootstrap starts at the
485		 * second sector of the boot area.  The first sector
486		 * contains the label and must be edited to contain the
487		 * size and location of the primary bootstrap.
488		 */
489		n = read(b, (char *)boot + dp->d_secsize,
490		    bbsize - dp->d_secsize);
491		if (n != bbsize - dp->d_secsize)
492			err(4, "%s", xxboot);
493		bootinfo = (uint64_t *)((char *)boot + 480);
494		bootinfo[0] = (n + dp->d_secsize - 1) / dp->d_secsize;
495		bootinfo[1] = 1;	/* start at sector 1 */
496		bootinfo[2] = 0;	/* flags (must be zero) */
497	} else {
498		if (sb.st_size != bbsize)
499			errx(4, "%s is wrong size, is %jd bytes, expected %d bytes",
500			    xxboot, (intmax_t)sb.st_size, bbsize);
501		n = read(b, (char *)boot, bbsize);
502		if (n != bbsize)
503			err(4, "%s", xxboot);
504	}
505
506	(void)close(b);
507	return (lp);
508}
509
510void
511display(FILE *f, const struct disklabel *lp)
512{
513	int i, j;
514	const struct partition *pp;
515
516	fprintf(f, "# %s:\n", specname);
517	if (lp->d_type < DKMAXTYPES)
518		fprintf(f, "type: %s\n", dktypenames[lp->d_type]);
519	else
520		fprintf(f, "type: %u\n", lp->d_type);
521	fprintf(f, "disk: %.*s\n", (int)sizeof(lp->d_typename),
522		lp->d_typename);
523	fprintf(f, "label: %.*s\n", (int)sizeof(lp->d_packname),
524		lp->d_packname);
525	fprintf(f, "flags:");
526	if (lp->d_flags & D_REMOVABLE)
527		fprintf(f, " removeable");
528	if (lp->d_flags & D_ECC)
529		fprintf(f, " ecc");
530	if (lp->d_flags & D_BADSECT)
531		fprintf(f, " badsect");
532	fprintf(f, "\n");
533	fprintf(f, "bytes/sector: %lu\n", (u_long)lp->d_secsize);
534	fprintf(f, "sectors/track: %lu\n", (u_long)lp->d_nsectors);
535	fprintf(f, "tracks/cylinder: %lu\n", (u_long)lp->d_ntracks);
536	fprintf(f, "sectors/cylinder: %lu\n", (u_long)lp->d_secpercyl);
537	fprintf(f, "cylinders: %lu\n", (u_long)lp->d_ncylinders);
538	fprintf(f, "sectors/unit: %lu\n", (u_long)lp->d_secperunit);
539	fprintf(f, "rpm: %u\n", lp->d_rpm);
540	fprintf(f, "interleave: %u\n", lp->d_interleave);
541	fprintf(f, "trackskew: %u\n", lp->d_trackskew);
542	fprintf(f, "cylinderskew: %u\n", lp->d_cylskew);
543	fprintf(f, "headswitch: %lu\t\t# milliseconds\n",
544	    (u_long)lp->d_headswitch);
545	fprintf(f, "track-to-track seek: %ld\t# milliseconds\n",
546	    (u_long)lp->d_trkseek);
547	fprintf(f, "drivedata: ");
548	for (i = NDDATA - 1; i >= 0; i--)
549		if (lp->d_drivedata[i])
550			break;
551	if (i < 0)
552		i = 0;
553	for (j = 0; j <= i; j++)
554		fprintf(f, "%lu ", (u_long)lp->d_drivedata[j]);
555	fprintf(f, "\n\n%u partitions:\n", lp->d_npartitions);
556	fprintf(f,
557	    "#        size   offset    fstype   [fsize bsize bps/cpg]\n");
558	pp = lp->d_partitions;
559	for (i = 0; i < lp->d_npartitions; i++, pp++) {
560		if (pp->p_size) {
561			fprintf(f, "  %c: %8lu %8lu  ", 'a' + i,
562			   (u_long)pp->p_size, (u_long)pp->p_offset);
563			if (pp->p_fstype < FSMAXTYPES)
564				fprintf(f, "%8.8s", fstypenames[pp->p_fstype]);
565			else
566				fprintf(f, "%8d", pp->p_fstype);
567			switch (pp->p_fstype) {
568
569			case FS_UNUSED:				/* XXX */
570				fprintf(f, "    %5lu %5lu %5.5s ",
571				    (u_long)pp->p_fsize,
572				    (u_long)(pp->p_fsize * pp->p_frag), "");
573				break;
574
575			case FS_BSDFFS:
576				fprintf(f, "    %5lu %5lu %5u ",
577				    (u_long)pp->p_fsize,
578				    (u_long)(pp->p_fsize * pp->p_frag),
579				    pp->p_cpg);
580				break;
581
582			case FS_BSDLFS:
583				fprintf(f, "    %5lu %5lu %5d",
584				    (u_long)pp->p_fsize,
585				    (u_long)(pp->p_fsize * pp->p_frag),
586				    pp->p_cpg);
587				break;
588
589			default:
590				fprintf(f, "%20.20s", "");
591				break;
592			}
593			fprintf(f, "\t# (Cyl. %4lu",
594			    (u_long)(pp->p_offset / lp->d_secpercyl));
595			if (pp->p_offset % lp->d_secpercyl)
596			    putc('*', f);
597			else
598			    putc(' ', f);
599			fprintf(f, "- %lu",
600			    (u_long)((pp->p_offset + pp->p_size +
601			    lp->d_secpercyl - 1) /
602			    lp->d_secpercyl - 1));
603			if (pp->p_size % lp->d_secpercyl)
604			    putc('*', f);
605			fprintf(f, ")\n");
606		}
607	}
608	fflush(f);
609}
610
611int
612edit(struct disklabel *lp, int f)
613{
614	int c, fd;
615	struct disklabel label;
616	FILE *fp;
617
618	if ((fd = mkstemp(tmpfil)) == -1 ||
619	    (fp = fdopen(fd, "w")) == NULL) {
620		warnx("can't create %s", tmpfil);
621		return (1);
622	}
623	display(fp, lp);
624	fclose(fp);
625	for (;;) {
626		if (!editit())
627			break;
628		fp = fopen(tmpfil, "r");
629		if (fp == NULL) {
630			warnx("can't reopen %s for reading", tmpfil);
631			break;
632		}
633		bzero((char *)&label, sizeof(label));
634		if (getasciilabel(fp, &label)) {
635			*lp = label;
636			if (writelabel(f, bootarea, lp) == 0) {
637				fclose(fp);
638				(void) unlink(tmpfil);
639				return (0);
640			}
641		}
642		fclose(fp);
643		printf("re-edit the label? [y]: "); fflush(stdout);
644		c = getchar();
645		if (c != EOF && c != (int)'\n')
646			while (getchar() != (int)'\n')
647				;
648		if  (c == (int)'n')
649			break;
650	}
651	(void) unlink(tmpfil);
652	return (1);
653}
654
655int
656editit(void)
657{
658	int pid, xpid;
659	int locstat, omask;
660	const char *ed;
661
662	omask = sigblock(sigmask(SIGINT)|sigmask(SIGQUIT)|sigmask(SIGHUP));
663	while ((pid = fork()) < 0) {
664		if (errno == EPROCLIM) {
665			warnx("you have too many processes");
666			return(0);
667		}
668		if (errno != EAGAIN) {
669			warn("fork");
670			return(0);
671		}
672		sleep(1);
673	}
674	if (pid == 0) {
675		sigsetmask(omask);
676		setgid(getgid());
677		setuid(getuid());
678		if ((ed = getenv("EDITOR")) == (char *)0)
679			ed = DEFEDITOR;
680		execlp(ed, ed, tmpfil, (char *)0);
681		err(1, "%s", ed);
682	}
683	while ((xpid = wait(&locstat)) >= 0)
684		if (xpid == pid)
685			break;
686	sigsetmask(omask);
687	return(!locstat);
688}
689
690char *
691skip(char *cp)
692{
693
694	while (*cp != '\0' && isspace(*cp))
695		cp++;
696	if (*cp == '\0' || *cp == '#')
697		return (NULL);
698	return (cp);
699}
700
701char *
702word(char *cp)
703{
704	char c;
705
706	while (*cp != '\0' && !isspace(*cp) && *cp != '#')
707		cp++;
708	if ((c = *cp) != '\0') {
709		*cp++ = '\0';
710		if (c != '#')
711			return (skip(cp));
712	}
713	return (NULL);
714}
715
716/*
717 * Read an ascii label in from fd f,
718 * in the same format as that put out by display(),
719 * and fill in lp.
720 */
721int
722getasciilabel(FILE *f, struct disklabel *lp)
723{
724	char *cp;
725	const char **cpp;
726	u_int part;
727	char *tp, line[BUFSIZ];
728	u_long v;
729	int lineno = 0, errors = 0;
730	int i;
731
732	bzero(&part_set, sizeof(part_set));
733	bzero(&part_size_type, sizeof(part_size_type));
734	bzero(&part_offset_type, sizeof(part_offset_type));
735	lp->d_bbsize = BBSIZE;				/* XXX */
736	lp->d_sbsize = 0;				/* XXX */
737	while (fgets(line, sizeof(line) - 1, f)) {
738		lineno++;
739		if ((cp = index(line,'\n')) != 0)
740			*cp = '\0';
741		cp = skip(line);
742		if (cp == NULL)
743			continue;
744		tp = index(cp, ':');
745		if (tp == NULL) {
746			fprintf(stderr, "line %d: syntax error\n", lineno);
747			errors++;
748			continue;
749		}
750		*tp++ = '\0', tp = skip(tp);
751		if (streq(cp, "type")) {
752			if (tp == NULL)
753				tp = unknown;
754			cpp = dktypenames;
755			for (; cpp < &dktypenames[DKMAXTYPES]; cpp++)
756				if (*cpp && streq(*cpp, tp)) {
757					lp->d_type = cpp - dktypenames;
758					break;
759				}
760			if (cpp < &dktypenames[DKMAXTYPES])
761				continue;
762			v = strtoul(tp, NULL, 10);
763			if (v >= DKMAXTYPES)
764				fprintf(stderr, "line %d:%s %lu\n", lineno,
765				    "Warning, unknown disk type", v);
766			lp->d_type = v;
767			continue;
768		}
769		if (streq(cp, "flags")) {
770			for (v = 0; (cp = tp) && *cp != '\0';) {
771				tp = word(cp);
772				if (streq(cp, "removeable"))
773					v |= D_REMOVABLE;
774				else if (streq(cp, "ecc"))
775					v |= D_ECC;
776				else if (streq(cp, "badsect"))
777					v |= D_BADSECT;
778				else {
779					fprintf(stderr,
780					    "line %d: %s: bad flag\n",
781					    lineno, cp);
782					errors++;
783				}
784			}
785			lp->d_flags = v;
786			continue;
787		}
788		if (streq(cp, "drivedata")) {
789			for (i = 0; (cp = tp) && *cp != '\0' && i < NDDATA;) {
790				lp->d_drivedata[i++] = strtoul(cp, NULL, 10);
791				tp = word(cp);
792			}
793			continue;
794		}
795		if (sscanf(cp, "%lu partitions", &v) == 1) {
796			if (v == 0 || v > MAXPARTITIONS) {
797				fprintf(stderr,
798				    "line %d: bad # of partitions\n", lineno);
799				lp->d_npartitions = MAXPARTITIONS;
800				errors++;
801			} else
802				lp->d_npartitions = v;
803			continue;
804		}
805		if (tp == NULL)
806			tp = blank;
807		if (streq(cp, "disk")) {
808			strncpy(lp->d_typename, tp, sizeof (lp->d_typename));
809			continue;
810		}
811		if (streq(cp, "label")) {
812			strncpy(lp->d_packname, tp, sizeof (lp->d_packname));
813			continue;
814		}
815		if (streq(cp, "bytes/sector")) {
816			v = strtoul(tp, NULL, 10);
817			if (v == 0 || (v % DEV_BSIZE) != 0) {
818				fprintf(stderr,
819				    "line %d: %s: bad sector size\n",
820				    lineno, tp);
821				errors++;
822			} else
823				lp->d_secsize = v;
824			continue;
825		}
826		if (streq(cp, "sectors/track")) {
827			v = strtoul(tp, NULL, 10);
828#if (ULONG_MAX != 0xffffffffUL)
829			if (v == 0 || v > 0xffffffff) {
830#else
831			if (v == 0) {
832#endif
833				fprintf(stderr, "line %d: %s: bad %s\n",
834				    lineno, tp, cp);
835				errors++;
836			} else
837				lp->d_nsectors = v;
838			continue;
839		}
840		if (streq(cp, "sectors/cylinder")) {
841			v = strtoul(tp, NULL, 10);
842			if (v == 0) {
843				fprintf(stderr, "line %d: %s: bad %s\n",
844				    lineno, tp, cp);
845				errors++;
846			} else
847				lp->d_secpercyl = v;
848			continue;
849		}
850		if (streq(cp, "tracks/cylinder")) {
851			v = strtoul(tp, NULL, 10);
852			if (v == 0) {
853				fprintf(stderr, "line %d: %s: bad %s\n",
854				    lineno, tp, cp);
855				errors++;
856			} else
857				lp->d_ntracks = v;
858			continue;
859		}
860		if (streq(cp, "cylinders")) {
861			v = strtoul(tp, NULL, 10);
862			if (v == 0) {
863				fprintf(stderr, "line %d: %s: bad %s\n",
864				    lineno, tp, cp);
865				errors++;
866			} else
867				lp->d_ncylinders = v;
868			continue;
869		}
870		if (streq(cp, "sectors/unit")) {
871			v = strtoul(tp, NULL, 10);
872			if (v == 0) {
873				fprintf(stderr, "line %d: %s: bad %s\n",
874				    lineno, tp, cp);
875				errors++;
876			} else
877				lp->d_secperunit = v;
878			continue;
879		}
880		if (streq(cp, "rpm")) {
881			v = strtoul(tp, NULL, 10);
882			if (v == 0 || v > USHRT_MAX) {
883				fprintf(stderr, "line %d: %s: bad %s\n",
884				    lineno, tp, cp);
885				errors++;
886			} else
887				lp->d_rpm = v;
888			continue;
889		}
890		if (streq(cp, "interleave")) {
891			v = strtoul(tp, NULL, 10);
892			if (v == 0 || v > USHRT_MAX) {
893				fprintf(stderr, "line %d: %s: bad %s\n",
894				    lineno, tp, cp);
895				errors++;
896			} else
897				lp->d_interleave = v;
898			continue;
899		}
900		if (streq(cp, "trackskew")) {
901			v = strtoul(tp, NULL, 10);
902			if (v > USHRT_MAX) {
903				fprintf(stderr, "line %d: %s: bad %s\n",
904				    lineno, tp, cp);
905				errors++;
906			} else
907				lp->d_trackskew = v;
908			continue;
909		}
910		if (streq(cp, "cylinderskew")) {
911			v = strtoul(tp, NULL, 10);
912			if (v > USHRT_MAX) {
913				fprintf(stderr, "line %d: %s: bad %s\n",
914				    lineno, tp, cp);
915				errors++;
916			} else
917				lp->d_cylskew = v;
918			continue;
919		}
920		if (streq(cp, "headswitch")) {
921			v = strtoul(tp, NULL, 10);
922			lp->d_headswitch = v;
923			continue;
924		}
925		if (streq(cp, "track-to-track seek")) {
926			v = strtoul(tp, NULL, 10);
927			lp->d_trkseek = v;
928			continue;
929		}
930		/* the ':' was removed above */
931		if (*cp < 'a' || *cp > MAX_PART || cp[1] != '\0') {
932			fprintf(stderr,
933			    "line %d: %s: Unknown disklabel field\n", lineno,
934			    cp);
935			errors++;
936			continue;
937		}
938
939		/* Process a partition specification line. */
940		part = *cp - 'a';
941		if (part >= lp->d_npartitions) {
942			fprintf(stderr,
943			    "line %d: partition name out of range a-%c: %s\n",
944			    lineno, 'a' + lp->d_npartitions - 1, cp);
945			errors++;
946			continue;
947		}
948		part_set[part] = 1;
949
950		if (getasciipartspec(tp, lp, part, lineno) != 0) {
951			errors++;
952			break;
953		}
954	}
955	errors += checklabel(lp);
956	return (errors == 0);
957}
958
959#define NXTNUM(n) do { \
960	if (tp == NULL) { \
961		fprintf(stderr, "line %d: too few numeric fields\n", lineno); \
962		return (1); \
963	} else { \
964		cp = tp, tp = word(cp); \
965		(n) = strtoul(cp, NULL, 10); \
966	} \
967} while (0)
968
969/* retain 1 character following number */
970#define NXTWORD(w,n) do { \
971	if (tp == NULL) { \
972		fprintf(stderr, "line %d: too few numeric fields\n", lineno); \
973		return (1); \
974	} else { \
975	        char *tmp; \
976		cp = tp, tp = word(cp); \
977	        (n) = strtoul(cp, &tmp, 10); \
978		if (tmp) (w) = *tmp; \
979	} \
980} while (0)
981
982/*
983 * Read a partition line into partition `part' in the specified disklabel.
984 * Return 0 on success, 1 on failure.
985 */
986int
987getasciipartspec(char *tp, struct disklabel *lp, int part, int lineno)
988{
989	struct partition *pp;
990	char *cp;
991	const char **cpp;
992	u_long v;
993
994	pp = &lp->d_partitions[part];
995	cp = NULL;
996
997	v = 0;
998	NXTWORD(part_size_type[part],v);
999	if (v == 0 && part_size_type[part] != '*') {
1000		fprintf(stderr,
1001		    "line %d: %s: bad partition size\n", lineno, cp);
1002		return (1);
1003	}
1004	pp->p_size = v;
1005
1006	v = 0;
1007	NXTWORD(part_offset_type[part],v);
1008	if (v == 0 && part_offset_type[part] != '*' &&
1009	    part_offset_type[part] != '\0') {
1010		fprintf(stderr,
1011		    "line %d: %s: bad partition offset\n", lineno, cp);
1012		return (1);
1013	}
1014	pp->p_offset = v;
1015	if (tp == NULL) {
1016		fprintf(stderr, "line %d: missing file system type\n", lineno);
1017		return (1);
1018	}
1019	cp = tp, tp = word(cp);
1020	for (cpp = fstypenames; cpp < &fstypenames[FSMAXTYPES]; cpp++)
1021		if (*cpp && streq(*cpp, cp))
1022			break;
1023	if (*cpp != NULL) {
1024		pp->p_fstype = cpp - fstypenames;
1025	} else {
1026		if (isdigit(*cp))
1027			v = strtoul(cp, NULL, 10);
1028		else
1029			v = FSMAXTYPES;
1030		if (v >= FSMAXTYPES) {
1031			fprintf(stderr,
1032			    "line %d: Warning, unknown file system type %s\n",
1033			    lineno, cp);
1034			v = FS_UNUSED;
1035		}
1036		pp->p_fstype = v;
1037	}
1038
1039	switch (pp->p_fstype) {
1040	case FS_UNUSED:
1041		/*
1042		 * allow us to accept defaults for
1043		 * fsize/frag/cpg
1044		 */
1045		if (tp) {
1046			NXTNUM(pp->p_fsize);
1047			if (pp->p_fsize == 0)
1048				break;
1049			NXTNUM(v);
1050			pp->p_frag = v / pp->p_fsize;
1051		}
1052		/* else default to 0's */
1053		break;
1054
1055	/* These happen to be the same */
1056	case FS_BSDFFS:
1057	case FS_BSDLFS:
1058		if (tp) {
1059			NXTNUM(pp->p_fsize);
1060			if (pp->p_fsize == 0)
1061				break;
1062			NXTNUM(v);
1063			pp->p_frag = v / pp->p_fsize;
1064			NXTNUM(pp->p_cpg);
1065		} else {
1066			/*
1067			 * FIX! poor attempt at adaptive
1068			 */
1069			/* 1 GB */
1070			if (pp->p_size < 1024*1024*1024 / lp->d_secsize) {
1071				/*
1072				 * FIX! These are too low, but are traditional
1073				 */
1074				pp->p_fsize = DEFAULT_NEWFS_FRAG;
1075				pp->p_frag = DEFAULT_NEWFS_BLOCK /
1076				    DEFAULT_NEWFS_FRAG;
1077				pp->p_cpg = DEFAULT_NEWFS_CPG;
1078			} else {
1079				pp->p_fsize = BIG_NEWFS_FRAG;
1080				pp->p_frag = BIG_NEWFS_BLOCK /
1081				    BIG_NEWFS_FRAG;
1082				pp->p_cpg = BIG_NEWFS_CPG;
1083			}
1084		}
1085	default:
1086		break;
1087	}
1088	return (0);
1089}
1090
1091/*
1092 * Check disklabel for errors and fill in
1093 * derived fields according to supplied values.
1094 */
1095int
1096checklabel(struct disklabel *lp)
1097{
1098	struct partition *pp;
1099	int i, errors = 0;
1100	char part;
1101	u_long total_size, total_percent, current_offset;
1102	int seen_default_offset;
1103	int hog_part;
1104	int j;
1105	struct partition *pp2;
1106
1107	if (lp->d_secsize == 0) {
1108		fprintf(stderr, "sector size 0\n");
1109		return (1);
1110	}
1111	if (lp->d_nsectors == 0) {
1112		fprintf(stderr, "sectors/track 0\n");
1113		return (1);
1114	}
1115	if (lp->d_ntracks == 0) {
1116		fprintf(stderr, "tracks/cylinder 0\n");
1117		return (1);
1118	}
1119	if  (lp->d_ncylinders == 0) {
1120		fprintf(stderr, "cylinders/unit 0\n");
1121		errors++;
1122	}
1123	if (lp->d_rpm == 0)
1124		Warning("revolutions/minute 0");
1125	if (lp->d_secpercyl == 0)
1126		lp->d_secpercyl = lp->d_nsectors * lp->d_ntracks;
1127	if (lp->d_secperunit == 0)
1128		lp->d_secperunit = lp->d_secpercyl * lp->d_ncylinders;
1129	if (lp->d_bbsize == 0) {
1130		fprintf(stderr, "boot block size 0\n");
1131		errors++;
1132	} else if (lp->d_bbsize % lp->d_secsize)
1133		Warning("boot block size %% sector-size != 0");
1134	if (lp->d_npartitions > MAXPARTITIONS)
1135		Warning("number of partitions (%lu) > MAXPARTITIONS (%d)",
1136		    (u_long)lp->d_npartitions, MAXPARTITIONS);
1137
1138	/* first allocate space to the partitions, then offsets */
1139	total_size = 0; /* in sectors */
1140	total_percent = 0; /* in percent */
1141	hog_part = -1;
1142	/* find all fixed partitions */
1143	for (i = 0; i < lp->d_npartitions; i++) {
1144		pp = &lp->d_partitions[i];
1145		if (part_set[i]) {
1146			if (part_size_type[i] == '*') {
1147				if (i == RAW_PART) {
1148					pp->p_size = lp->d_secperunit;
1149				} else {
1150					if (hog_part != -1)
1151						Warning("Too many '*' partitions (%c and %c)",
1152						    hog_part + 'a',i + 'a');
1153					else
1154						hog_part = i;
1155				}
1156			} else {
1157				off_t size;
1158
1159				size = pp->p_size;
1160				switch (part_size_type[i]) {
1161				case '%':
1162					total_percent += size;
1163					break;
1164				case 'k':
1165				case 'K':
1166					size *= 1024ULL;
1167					break;
1168				case 'm':
1169				case 'M':
1170					size *= 1024ULL * 1024ULL;
1171					break;
1172				case 'g':
1173				case 'G':
1174					size *= 1024ULL * 1024ULL * 1024ULL;
1175					break;
1176				case '\0':
1177					break;
1178				default:
1179					Warning("unknown size specifier '%c' (K/M/G are valid)",part_size_type[i]);
1180					break;
1181				}
1182				/* don't count %'s yet */
1183				if (part_size_type[i] != '%') {
1184					/*
1185					 * for all not in sectors, convert to
1186					 * sectors
1187					 */
1188					if (part_size_type[i] != '\0') {
1189						if (size % lp->d_secsize != 0)
1190							Warning("partition %c not an integer number of sectors",
1191							    i + 'a');
1192						size /= lp->d_secsize;
1193						pp->p_size = size;
1194					}
1195					/* else already in sectors */
1196					if (i != RAW_PART)
1197						total_size += size;
1198				}
1199			}
1200		}
1201	}
1202	/* handle % partitions - note %'s don't need to add up to 100! */
1203	if (total_percent != 0) {
1204		long free_space = lp->d_secperunit - total_size;
1205		if (total_percent > 100) {
1206			fprintf(stderr,"total percentage %lu is greater than 100\n",
1207			    total_percent);
1208			errors++;
1209		}
1210
1211		if (free_space > 0) {
1212			for (i = 0; i < lp->d_npartitions; i++) {
1213				pp = &lp->d_partitions[i];
1214				if (part_set[i] && part_size_type[i] == '%') {
1215					/* careful of overflows! and integer roundoff */
1216					pp->p_size = ((double)pp->p_size/100) * free_space;
1217					total_size += pp->p_size;
1218
1219					/* FIX we can lose a sector or so due to roundoff per
1220					   partition.  A more complex algorithm could avoid that */
1221				}
1222			}
1223		} else {
1224			fprintf(stderr,
1225			    "%ld sectors available to give to '*' and '%%' partitions\n",
1226			    free_space);
1227			errors++;
1228			/* fix?  set all % partitions to size 0? */
1229		}
1230	}
1231	/* give anything remaining to the hog partition */
1232	if (hog_part != -1) {
1233		lp->d_partitions[hog_part].p_size = lp->d_secperunit - total_size;
1234		total_size = lp->d_secperunit;
1235	}
1236
1237	/* Now set the offsets for each partition */
1238	current_offset = 0; /* in sectors */
1239	seen_default_offset = 0;
1240	for (i = 0; i < lp->d_npartitions; i++) {
1241		part = 'a' + i;
1242		pp = &lp->d_partitions[i];
1243		if (part_set[i]) {
1244			if (part_offset_type[i] == '*') {
1245				if (i == RAW_PART) {
1246					pp->p_offset = 0;
1247				} else {
1248					pp->p_offset = current_offset;
1249					seen_default_offset = 1;
1250				}
1251			} else {
1252				/* allow them to be out of order for old-style tables */
1253				if (pp->p_offset < current_offset &&
1254				    seen_default_offset && i != RAW_PART &&
1255				    pp->p_fstype != FS_VINUM) {
1256					fprintf(stderr,
1257"Offset %ld for partition %c overlaps previous partition which ends at %lu\n",
1258					    (long)pp->p_offset,i+'a',current_offset);
1259					fprintf(stderr,
1260"Labels with any *'s for offset must be in ascending order by sector\n");
1261					errors++;
1262				} else if (pp->p_offset != current_offset &&
1263				    i != RAW_PART && seen_default_offset) {
1264					/*
1265					 * this may give unneeded warnings if
1266					 * partitions are out-of-order
1267					 */
1268					Warning(
1269"Offset %ld for partition %c doesn't match expected value %ld",
1270					    (long)pp->p_offset, i + 'a', current_offset);
1271				}
1272			}
1273			if (i != RAW_PART)
1274				current_offset = pp->p_offset + pp->p_size;
1275		}
1276	}
1277
1278	for (i = 0; i < lp->d_npartitions; i++) {
1279		part = 'a' + i;
1280		pp = &lp->d_partitions[i];
1281		if (pp->p_size == 0 && pp->p_offset != 0)
1282			Warning("partition %c: size 0, but offset %lu",
1283			    part, (u_long)pp->p_offset);
1284#ifdef notdef
1285		if (pp->p_size % lp->d_secpercyl)
1286			Warning("partition %c: size %% cylinder-size != 0",
1287			    part);
1288		if (pp->p_offset % lp->d_secpercyl)
1289			Warning("partition %c: offset %% cylinder-size != 0",
1290			    part);
1291#endif
1292		if (pp->p_offset > lp->d_secperunit) {
1293			fprintf(stderr,
1294			    "partition %c: offset past end of unit\n", part);
1295			errors++;
1296		}
1297		if (pp->p_offset + pp->p_size > lp->d_secperunit) {
1298			fprintf(stderr,
1299			"partition %c: partition extends past end of unit\n",
1300			    part);
1301			errors++;
1302		}
1303		if (i == RAW_PART)
1304		{
1305			if (pp->p_fstype != FS_UNUSED)
1306				Warning("partition %c is not marked as unused!",part);
1307			if (pp->p_offset != 0)
1308				Warning("partition %c doesn't start at 0!",part);
1309			if (pp->p_size != lp->d_secperunit)
1310				Warning("partition %c doesn't cover the whole unit!",part);
1311
1312			if ((pp->p_fstype != FS_UNUSED) || (pp->p_offset != 0) ||
1313			    (pp->p_size != lp->d_secperunit)) {
1314				Warning("An incorrect partition %c may cause problems for "
1315				    "standard system utilities",part);
1316			}
1317		}
1318
1319		/* check for overlaps */
1320		/* this will check for all possible overlaps once and only once */
1321		for (j = 0; j < i; j++) {
1322			pp2 = &lp->d_partitions[j];
1323			if (j != RAW_PART && i != RAW_PART &&
1324			    pp->p_fstype != FS_VINUM &&
1325			    pp2->p_fstype != FS_VINUM &&
1326			    part_set[i] && part_set[j]) {
1327				if (pp2->p_offset < pp->p_offset + pp->p_size &&
1328				    (pp2->p_offset + pp2->p_size > pp->p_offset ||
1329					pp2->p_offset >= pp->p_offset)) {
1330					fprintf(stderr,"partitions %c and %c overlap!\n",
1331					    j + 'a', i + 'a');
1332					errors++;
1333				}
1334			}
1335		}
1336	}
1337	for (; i < MAXPARTITIONS; i++) {
1338		part = 'a' + i;
1339		pp = &lp->d_partitions[i];
1340		if (pp->p_size || pp->p_offset)
1341			Warning("unused partition %c: size %d offset %lu",
1342			    'a' + i, pp->p_size, (u_long)pp->p_offset);
1343	}
1344	return (errors);
1345}
1346
1347/*
1348 * When operating on a "virgin" disk, try getting an initial label
1349 * from the associated device driver.  This might work for all device
1350 * drivers that are able to fetch some initial device parameters
1351 * without even having access to a (BSD) disklabel, like SCSI disks,
1352 * most IDE drives, or vn devices.
1353 *
1354 * The device name must be given in its "canonical" form.
1355 */
1356struct disklabel *
1357getvirginlabel(void)
1358{
1359	static struct disklabel loclab;
1360	struct partition *dp;
1361	char lnamebuf[BBSIZE];
1362	int f;
1363	u_int secsize, u;
1364	off_t mediasize;
1365
1366	if (dkname[0] == '/') {
1367		warnx("\"auto\" requires the usage of a canonical disk name");
1368		return (NULL);
1369	}
1370	(void)snprintf(lnamebuf, BBSIZE, "%s%s", _PATH_DEV, dkname);
1371	if ((f = open(lnamebuf, O_RDONLY)) == -1) {
1372		warn("cannot open %s", lnamebuf);
1373		return (NULL);
1374	}
1375
1376	/* New world order */
1377	if ((ioctl(f, DIOCGMEDIASIZE, &mediasize) != 0) ||
1378	    (ioctl(f, DIOCGSECTORSIZE, &secsize) != 0)) {
1379		close (f);
1380		return (NULL);
1381	}
1382	memset(&loclab, 0, sizeof loclab);
1383	loclab.d_magic = DISKMAGIC;
1384	loclab.d_magic2 = DISKMAGIC;
1385	loclab.d_secsize = secsize;
1386	loclab.d_secperunit = mediasize / secsize;
1387
1388	/*
1389	 * Nobody in these enligthened days uses the CHS geometry for
1390	 * anything, but nontheless try to get it right.  If we fail
1391	 * to get any good ideas from the device, construct something
1392	 * which is IBM-PC friendly.
1393	 */
1394	if (ioctl(f, DIOCGFWSECTORS, &u) == 0)
1395		loclab.d_nsectors = u;
1396	else
1397		loclab.d_nsectors = 63;
1398	if (ioctl(f, DIOCGFWHEADS, &u) == 0)
1399		loclab.d_ntracks = u;
1400	else if (loclab.d_secperunit <= 63*1*1024)
1401		loclab.d_ntracks = 1;
1402	else if (loclab.d_secperunit <= 63*16*1024)
1403		loclab.d_ntracks = 16;
1404	else
1405		loclab.d_ntracks = 255;
1406	loclab.d_secpercyl = loclab.d_ntracks * loclab.d_nsectors;
1407	loclab.d_ncylinders = loclab.d_secperunit / loclab.d_secpercyl;
1408	loclab.d_npartitions = MAXPARTITIONS;
1409
1410	/* Various (unneeded) compat stuff */
1411	loclab.d_rpm = 3600;
1412	loclab.d_bbsize = BBSIZE;
1413	loclab.d_interleave = 1;;
1414	strncpy(loclab.d_typename, "amnesiac",
1415	    sizeof(loclab.d_typename));
1416
1417	dp = &loclab.d_partitions[RAW_PART];
1418	dp->p_size = loclab.d_secperunit;
1419	loclab.d_checksum = dkcksum(&loclab);
1420	close (f);
1421	return (&loclab);
1422}
1423
1424
1425/*VARARGS1*/
1426void
1427Warning(const char *fmt, ...)
1428{
1429	va_list ap;
1430
1431	fprintf(stderr, "Warning, ");
1432	va_start(ap, fmt);
1433	vfprintf(stderr, fmt, ap);
1434	fprintf(stderr, "\n");
1435	va_end(ap);
1436}
1437
1438void
1439usage(void)
1440{
1441
1442	fprintf(stderr,
1443	"%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",
1444	"usage: bsdlabel [-r] disk",
1445	"\t\t(to read label)",
1446	"	bsdlabel -w [-nr] [-m machine] disk type [packid]",
1447	"\t\t(to write label with existing boot program)",
1448	"	bsdlabel -e [-nr] [-m machine] disk",
1449	"\t\t(to edit label)",
1450	"	bsdlabel -R [-nr] [-m machine] disk protofile",
1451	"\t\t(to restore label with existing boot program)",
1452	"	bsdlabel -B [-b boot] [-m machine] disk",
1453	"\t\t(to install boot program with existing on-disk label)",
1454	"	bsdlabel -w -B [-n] [-b boot] [-m machine] disk type [packid]",
1455	"\t\t(to write label and install boot program)",
1456	"	bsdlabel -R -B [-n] [-b boot] [-m machine] disk protofile",
1457		"\t\t(to restore label and install boot program)"
1458	);
1459	exit(1);
1460}
1461