bsdlabel.c revision 55742
1/*
2 * Copyright (c) 1987, 1993
3 *	The Regents of the University of California.  All rights reserved.
4 *
5 * This code is derived from software contributed to Berkeley by
6 * Symmetric Computer Systems.
7 *
8 * Redistribution and use in source and binary forms, with or without
9 * modification, are permitted provided that the following conditions
10 * are met:
11 * 1. Redistributions of source code must retain the above copyright
12 *    notice, this list of conditions and the following disclaimer.
13 * 2. Redistributions in binary form must reproduce the above copyright
14 *    notice, this list of conditions and the following disclaimer in the
15 *    documentation and/or other materials provided with the distribution.
16 * 3. All advertising materials mentioning features or use of this software
17 *    must display the following acknowledgement:
18 *	This product includes software developed by the University of
19 *	California, Berkeley and its contributors.
20 * 4. Neither the name of the University nor the names of its contributors
21 *    may be used to endorse or promote products derived from this software
22 *    without specific prior written permission.
23 *
24 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
25 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
26 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
27 * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
28 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
29 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
30 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
31 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
32 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
33 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
34 * SUCH DAMAGE.
35 */
36
37#ifndef lint
38static const char copyright[] =
39"@(#) Copyright (c) 1987, 1993\n\
40	The Regents of the University of California.  All rights reserved.\n";
41#endif /* not lint */
42
43#ifndef lint
44#if 0
45static char sccsid[] = "@(#)disklabel.c	8.2 (Berkeley) 1/7/94";
46/* from static char sccsid[] = "@(#)disklabel.c	1.2 (Symmetric) 11/28/85"; */
47#endif
48static const char rcsid[] =
49  "$FreeBSD: head/sbin/bsdlabel/bsdlabel.c 55742 2000-01-10 09:25:32Z kris $";
50#endif /* not lint */
51
52#include <sys/param.h>
53#include <sys/errno.h>
54#include <sys/file.h>
55#include <sys/stat.h>
56#include <sys/wait.h>
57#define DKTYPENAMES
58#include <sys/disklabel.h>
59#include <ufs/ffs/fs.h>
60#include <unistd.h>
61#include <string.h>
62#include <stdio.h>
63#include <stdlib.h>
64#include <signal.h>
65#include <stdarg.h>
66#include <ctype.h>
67#include <err.h>
68#include "pathnames.h"
69
70/*
71 * Disklabel: read and write disklabels.
72 * The label is usually placed on one of the first sectors of the disk.
73 * Many machines also place a bootstrap in the same area,
74 * in which case the label is embedded in the bootstrap.
75 * The bootstrap source must leave space at the proper offset
76 * for the label on such machines.
77 */
78
79#ifndef BBSIZE
80#define	BBSIZE	8192			/* size of boot area, with label */
81#endif
82
83#ifdef tahoe
84#define	NUMBOOT	0
85#else
86#if defined(__alpha__) || defined(hp300) || defined(hp800)
87#define	NUMBOOT	1
88#else
89#define	NUMBOOT	2
90#endif
91#endif
92
93void	makelabel	__P((char *, char *, struct disklabel *));
94int	writelabel	__P((int, char *, struct disklabel *));
95void	l_perror	__P((char *));
96struct disklabel * readlabel __P((int));
97struct disklabel * makebootarea __P((char *, struct disklabel *, int));
98void	display		__P((FILE *, struct disklabel *));
99int	edit		__P((struct disklabel *, int));
100int	editit		__P((void));
101char *	skip		__P((char *));
102char *	word		__P((char *));
103int	getasciilabel	__P((FILE *, struct disklabel *));
104int	checklabel	__P((struct disklabel *));
105void	setbootflag	__P((struct disklabel *));
106void	Warning		(char *, ...);
107void	usage		__P((void));
108extern	u_short dkcksum __P((struct disklabel *));
109struct disklabel * getvirginlabel __P((void));
110
111#define	DEFEDITOR	_PATH_VI
112#define	streq(a,b)	(strcmp(a,b) == 0)
113
114char	*dkname;
115char	*specname;
116char	tmpfil[] = PATH_TMPFILE;
117
118char	namebuf[BBSIZE], *np = namebuf;
119struct	disklabel lab;
120char	bootarea[BBSIZE];
121
122#if NUMBOOT > 0
123int	installboot;	/* non-zero if we should install a boot program */
124char	*bootbuf;	/* pointer to buffer with remainder of boot prog */
125int	bootsize;	/* size of remaining boot program */
126char	*xxboot;	/* primary boot */
127char	*bootxx;	/* secondary boot */
128char	boot0[MAXPATHLEN];
129char	boot1[MAXPATHLEN];
130#endif
131
132enum	{
133	UNSPEC, EDIT, NOWRITE, READ, RESTORE, WRITE, WRITEABLE, WRITEBOOT
134} op = UNSPEC;
135
136int	rflag;
137
138#ifdef DEBUG
139int	debug;
140#define OPTIONS	"BNRWb:ders:w"
141#else
142#define OPTIONS	"BNRWb:ers:w"
143#endif
144
145int
146main(argc, argv)
147	int argc;
148	char *argv[];
149{
150	register struct disklabel *lp;
151	FILE *t;
152	int ch, f = 0, flag, error = 0;
153	char *name = 0;
154
155	while ((ch = getopt(argc, argv, OPTIONS)) != -1)
156		switch (ch) {
157#if NUMBOOT > 0
158			case 'B':
159				++installboot;
160				break;
161			case 'b':
162				xxboot = optarg;
163				break;
164#if NUMBOOT > 1
165			case 's':
166				bootxx = optarg;
167				break;
168#endif
169#endif
170			case 'N':
171				if (op != UNSPEC)
172					usage();
173				op = NOWRITE;
174				break;
175			case 'R':
176				if (op != UNSPEC)
177					usage();
178				op = RESTORE;
179				break;
180			case 'W':
181				if (op != UNSPEC)
182					usage();
183				op = WRITEABLE;
184				break;
185			case 'e':
186				if (op != UNSPEC)
187					usage();
188				op = EDIT;
189				break;
190			case 'r':
191				++rflag;
192				break;
193			case 'w':
194				if (op != UNSPEC)
195					usage();
196				op = WRITE;
197				break;
198#ifdef DEBUG
199			case 'd':
200				debug++;
201				break;
202#endif
203			case '?':
204			default:
205				usage();
206		}
207	argc -= optind;
208	argv += optind;
209#if NUMBOOT > 0
210	if (installboot) {
211		rflag++;
212		if (op == UNSPEC)
213			op = WRITEBOOT;
214	} else {
215		if (op == UNSPEC)
216			op = READ;
217		xxboot = bootxx = 0;
218	}
219#else
220	if (op == UNSPEC)
221		op = READ;
222#endif
223	if (argc < 1)
224		usage();
225
226	dkname = argv[0];
227	if (dkname[0] != '/') {
228		(void)sprintf(np, "%sr%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, "%sr%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)
240		err(4, "%s", specname);
241
242	switch(op) {
243
244	case UNSPEC:
245		break;
246
247	case EDIT:
248		if (argc != 1)
249			usage();
250		lp = readlabel(f);
251		error = edit(lp, f);
252		break;
253
254	case NOWRITE:
255		flag = 0;
256		if (ioctl(f, DIOCWLABEL, (char *)&flag) < 0)
257			err(4, "ioctl DIOCWLABEL");
258		break;
259
260	case READ:
261		if (argc != 1)
262			usage();
263		lp = readlabel(f);
264		display(stdout, lp);
265		error = checklabel(lp);
266		break;
267
268	case RESTORE:
269#if NUMBOOT > 0
270		if (installboot && argc == 3) {
271			makelabel(argv[2], 0, &lab);
272			argc--;
273
274			/*
275			 * We only called makelabel() for its side effect
276			 * of setting the bootstrap file names.  Discard
277			 * all changes to `lab' so that all values in the
278			 * final label come from the ASCII label.
279			 */
280			bzero((char *)&lab, sizeof(lab));
281		}
282#endif
283		if (argc != 2)
284			usage();
285		if (!(t = fopen(argv[1], "r")))
286			err(4, "%s", argv[1]);
287		if (!getasciilabel(t, &lab))
288			exit(1);
289		lp = makebootarea(bootarea, &lab, f);
290		*lp = lab;
291		error = writelabel(f, bootarea, lp);
292		break;
293
294	case WRITE:
295		if (argc == 3) {
296			name = argv[2];
297			argc--;
298		}
299		if (argc != 2)
300			usage();
301		makelabel(argv[1], name, &lab);
302		lp = makebootarea(bootarea, &lab, f);
303		*lp = lab;
304		if (checklabel(lp) == 0)
305			error = writelabel(f, bootarea, lp);
306		break;
307
308	case WRITEABLE:
309		flag = 1;
310		if (ioctl(f, DIOCWLABEL, (char *)&flag) < 0)
311			err(4, "ioctl DIOCWLABEL");
312		break;
313
314#if NUMBOOT > 0
315	case WRITEBOOT:
316	{
317		struct disklabel tlab;
318
319		lp = readlabel(f);
320		tlab = *lp;
321		if (argc == 2)
322			makelabel(argv[1], 0, &lab);
323		lp = makebootarea(bootarea, &lab, f);
324		*lp = tlab;
325		if (checklabel(lp) == 0)
326			error = writelabel(f, bootarea, lp);
327		break;
328	}
329#endif
330	}
331	exit(error);
332}
333
334/*
335 * Construct a prototype disklabel from /etc/disktab.  As a side
336 * effect, set the names of the primary and secondary boot files
337 * if specified.
338 */
339void
340makelabel(type, name, lp)
341	char *type, *name;
342	register struct disklabel *lp;
343{
344	register struct disklabel *dp;
345
346	if (strcmp(type, "auto") == 0)
347		dp = getvirginlabel();
348	else
349		dp = getdiskbyname(type);
350	if (dp == NULL)
351		errx(1, "%s: unknown disk type", type);
352	*lp = *dp;
353#if NUMBOOT > 0
354	/*
355	 * Set bootstrap name(s).
356	 * 1. If set from command line, use those,
357	 * 2. otherwise, check if disktab specifies them (b0 or b1),
358	 * 3. otherwise, makebootarea() will choose ones based on the name
359	 *    of the disk special file. E.g. /dev/ra0 -> raboot, bootra
360	 */
361	if (!xxboot && lp->d_boot0) {
362		if (*lp->d_boot0 != '/')
363			(void)sprintf(boot0, "%s/%s",
364				      _PATH_BOOTDIR, lp->d_boot0);
365		else
366			(void)strcpy(boot0, lp->d_boot0);
367		xxboot = boot0;
368	}
369#if NUMBOOT > 1
370	if (!bootxx && lp->d_boot1) {
371		if (*lp->d_boot1 != '/')
372			(void)sprintf(boot1, "%s/%s",
373				      _PATH_BOOTDIR, lp->d_boot1);
374		else
375			(void)strcpy(boot1, lp->d_boot1);
376		bootxx = boot1;
377	}
378#endif
379#endif
380	/* d_packname is union d_boot[01], so zero */
381	bzero(lp->d_packname, sizeof(lp->d_packname));
382	if (name)
383		(void)strncpy(lp->d_packname, name, sizeof(lp->d_packname));
384}
385
386int
387writelabel(f, boot, lp)
388	int f;
389	char *boot;
390	register struct disklabel *lp;
391{
392	int flag;
393#ifdef __alpha__
394	u_long *p, sum;
395	int i;
396#endif
397#ifdef vax
398	register int i;
399#endif
400
401	setbootflag(lp);
402	lp->d_magic = DISKMAGIC;
403	lp->d_magic2 = DISKMAGIC;
404	lp->d_checksum = 0;
405	lp->d_checksum = dkcksum(lp);
406	if (rflag) {
407		/*
408		 * First set the kernel disk label,
409		 * then write a label to the raw disk.
410		 * If the SDINFO ioctl fails because it is unimplemented,
411		 * keep going; otherwise, the kernel consistency checks
412		 * may prevent us from changing the current (in-core)
413		 * label.
414		 */
415		if (ioctl(f, DIOCSDINFO, lp) < 0 &&
416		    errno != ENODEV && errno != ENOTTY) {
417			l_perror("ioctl DIOCSDINFO");
418			return (1);
419		}
420		(void)lseek(f, (off_t)0, SEEK_SET);
421
422#ifdef __alpha__
423		/*
424		 * Generate the bootblock checksum for the SRM console.
425		 */
426		for (p = (u_long *)boot, i = 0, sum = 0; i < 63; i++)
427			sum += p[i];
428		p[63] = sum;
429#endif
430
431		/*
432		 * write enable label sector before write (if necessary),
433		 * disable after writing.
434		 */
435		flag = 1;
436		if (ioctl(f, DIOCWLABEL, &flag) < 0)
437			warn("ioctl DIOCWLABEL");
438		if (write(f, boot, lp->d_bbsize) != lp->d_bbsize) {
439			warn("write");
440			return (1);
441		}
442#if NUMBOOT > 0
443		/*
444		 * Output the remainder of the disklabel
445		 */
446		if (bootbuf && write(f, bootbuf, bootsize) != bootsize) {
447			warn("write");
448			return(1);
449		}
450#endif
451		flag = 0;
452		(void) ioctl(f, DIOCWLABEL, &flag);
453	} else if (ioctl(f, DIOCWDINFO, lp) < 0) {
454		l_perror("ioctl DIOCWDINFO");
455		return (1);
456	}
457#ifdef vax
458	if (lp->d_type == DTYPE_SMD && lp->d_flags & D_BADSECT) {
459		daddr_t alt;
460
461		alt = lp->d_ncylinders * lp->d_secpercyl - lp->d_nsectors;
462		for (i = 1; i < 11 && i < lp->d_nsectors; i += 2) {
463			(void)lseek(f, (off_t)((alt + i) * lp->d_secsize),
464			    SEEK_SET);
465			if (write(f, boot, lp->d_secsize) < lp->d_secsize)
466				warn("alternate label %d write", i/2);
467		}
468	}
469#endif
470	return (0);
471}
472
473void
474l_perror(s)
475	char *s;
476{
477	switch (errno) {
478
479	case ESRCH:
480		warnx("%s: no disk label on disk;", s);
481		fprintf(stderr,
482		    "use \"disklabel -r\" to install initial label\n");
483		break;
484
485	case EINVAL:
486		warnx("%s: label magic number or checksum is wrong!", s);
487		fprintf(stderr, "(disklabel or kernel is out of date?)\n");
488		break;
489
490	case EBUSY:
491		warnx("%s: open partition would move or shrink", s);
492		break;
493
494	case EXDEV:
495		warnx("%s: '%c' partition must start at beginning of disk",
496		    s, 'a' + RAW_PART);
497		break;
498
499	default:
500		warn((char *)NULL);
501		break;
502	}
503}
504
505/*
506 * Fetch disklabel for disk.
507 * Use ioctl to get label unless -r flag is given.
508 */
509struct disklabel *
510readlabel(f)
511	int f;
512{
513	register struct disklabel *lp;
514
515	if (rflag) {
516		if (read(f, bootarea, BBSIZE) < BBSIZE)
517			err(4, "%s", specname);
518		for (lp = (struct disklabel *)bootarea;
519		    lp <= (struct disklabel *)(bootarea + BBSIZE - sizeof(*lp));
520		    lp = (struct disklabel *)((char *)lp + 16))
521			if (lp->d_magic == DISKMAGIC &&
522			    lp->d_magic2 == DISKMAGIC)
523				break;
524		if (lp > (struct disklabel *)(bootarea+BBSIZE-sizeof(*lp)) ||
525		    lp->d_magic != DISKMAGIC || lp->d_magic2 != DISKMAGIC ||
526		    dkcksum(lp) != 0)
527			errx(1,
528	    "bad pack magic number (label is damaged, or pack is unlabeled)");
529	} else {
530		lp = &lab;
531		if (ioctl(f, DIOCGDINFO, lp) < 0)
532			err(4, "ioctl DIOCGDINFO");
533	}
534	return (lp);
535}
536
537/*
538 * Construct a bootarea (d_bbsize bytes) in the specified buffer ``boot''
539 * Returns a pointer to the disklabel portion of the bootarea.
540 */
541struct disklabel *
542makebootarea(boot, dp, f)
543	char *boot;
544	register struct disklabel *dp;
545	int f;
546{
547	struct disklabel *lp;
548	register char *p;
549	int b;
550#if NUMBOOT > 0
551	char *dkbasename;
552	struct stat sb;
553#endif
554#ifdef __alpha__
555	u_long *bootinfo;
556	int n;
557#endif
558#ifdef __i386__
559	char *tmpbuf;
560	int i, found;
561#endif
562
563	/* XXX */
564	if (dp->d_secsize == 0) {
565		dp->d_secsize = DEV_BSIZE;
566		dp->d_bbsize = BBSIZE;
567	}
568	lp = (struct disklabel *)
569		(boot + (LABELSECTOR * dp->d_secsize) + LABELOFFSET);
570	bzero((char *)lp, sizeof *lp);
571#if NUMBOOT > 0
572	/*
573	 * If we are not installing a boot program but we are installing a
574	 * label on disk then we must read the current bootarea so we don't
575	 * clobber the existing boot.
576	 */
577	if (!installboot) {
578		if (rflag) {
579			if (read(f, boot, BBSIZE) < BBSIZE)
580				err(4, "%s", specname);
581			bzero((char *)lp, sizeof *lp);
582		}
583		return (lp);
584	}
585	/*
586	 * We are installing a boot program.  Determine the name(s) and
587	 * read them into the appropriate places in the boot area.
588	 */
589	if (!xxboot || !bootxx) {
590		dkbasename = np;
591		if ((p = rindex(dkname, '/')) == NULL)
592			p = dkname;
593		else
594			p++;
595		while (*p && !isdigit(*p))
596			*np++ = *p++;
597		*np++ = '\0';
598
599		if (!xxboot) {
600			(void)sprintf(boot0, "%s/boot1", _PATH_BOOTDIR);
601			xxboot = boot0;
602		}
603#if NUMBOOT > 1
604		if (!bootxx) {
605			(void)sprintf(boot1, "%s/boot2", _PATH_BOOTDIR);
606			bootxx = boot1;
607		}
608#endif
609	}
610#ifdef DEBUG
611	if (debug)
612		fprintf(stderr, "bootstraps: xxboot = %s, bootxx = %s\n",
613			xxboot, bootxx ? bootxx : "NONE");
614#endif
615
616	/*
617	 * Strange rules:
618	 * 1. One-piece bootstrap (hp300/hp800)
619	 *	up to d_bbsize bytes of ``xxboot'' go in bootarea, the rest
620	 *	is remembered and written later following the bootarea.
621	 * 2. Two-piece bootstraps (vax/i386?/mips?)
622	 *	up to d_secsize bytes of ``xxboot'' go in first d_secsize
623	 *	bytes of bootarea, remaining d_bbsize-d_secsize filled
624	 *	from ``bootxx''.
625	 */
626	b = open(xxboot, O_RDONLY);
627	if (b < 0)
628		err(4, "%s", xxboot);
629#if NUMBOOT > 1
630#ifdef __i386__
631	/*
632	 * XXX Botch alert.
633	 * The i386 has the so-called fdisk table embedded into the
634	 * primary bootstrap.  We take care to not clobber it, but
635	 * only if it does already contain some data.  (Otherwise,
636	 * the xxboot provides a template.)
637	 */
638	if ((tmpbuf = (char *)malloc((int)dp->d_secsize)) == 0)
639		err(4, "%s", xxboot);
640	memcpy((void *)tmpbuf, (void *)boot, (int)dp->d_secsize);
641#endif /* i386 */
642	if (read(b, boot, (int)dp->d_secsize) < 0)
643		err(4, "%s", xxboot);
644	(void)close(b);
645#ifdef __i386__
646	for (i = DOSPARTOFF, found = 0;
647	     !found && i < DOSPARTOFF + NDOSPART*sizeof(struct dos_partition);
648	     i++)
649		found = tmpbuf[i] != 0;
650	if (found)
651		memcpy((void *)&boot[DOSPARTOFF],
652		       (void *)&tmpbuf[DOSPARTOFF],
653		       NDOSPART * sizeof(struct dos_partition));
654	free(tmpbuf);
655#endif /* i386 */
656	b = open(bootxx, O_RDONLY);
657	if (b < 0)
658		err(4, "%s", bootxx);
659	if (fstat(b, &sb) != 0)
660		err(4, "%s", bootxx);
661	if (dp->d_secsize + sb.st_size > dp->d_bbsize)
662		errx(4, "%s too large", bootxx);
663	if (read(b, &boot[dp->d_secsize],
664		 (int)(dp->d_bbsize-dp->d_secsize)) < 0)
665		err(4, "%s", bootxx);
666#else /* !(NUMBOOT > 1) */
667#ifdef __alpha__
668	/*
669	 * On the alpha, the primary bootstrap starts at the
670	 * second sector of the boot area.  The first sector
671	 * contains the label and must be edited to contain the
672	 * size and location of the primary bootstrap.
673	 */
674	n = read(b, boot + dp->d_secsize, (int)dp->d_bbsize);
675	if (n < 0)
676		err(4, "%s", xxboot);
677	bootinfo = (u_long *)(boot + 480);
678	bootinfo[0] = (n + dp->d_secsize - 1) / dp->d_secsize;
679	bootinfo[1] = 1;	/* start at sector 1 */
680	bootinfo[2] = 0;	/* flags (must be zero) */
681#else /* !__alpha__ */
682	if (read(b, boot, (int)dp->d_bbsize) < 0)
683		err(4, "%s", xxboot);
684#endif /* __alpha__ */
685	if (fstat(b, &sb) != 0)
686		err(4, "%s", xxboot);
687	bootsize = (int)sb.st_size - dp->d_bbsize;
688	if (bootsize > 0) {
689		/* XXX assume d_secsize is a power of two */
690		bootsize = (bootsize + dp->d_secsize-1) & ~(dp->d_secsize-1);
691		bootbuf = (char *)malloc((size_t)bootsize);
692		if (bootbuf == 0)
693			err(4, "%s", xxboot);
694		if (read(b, bootbuf, bootsize) < 0) {
695			free(bootbuf);
696			err(4, "%s", xxboot);
697		}
698	}
699#endif /* NUMBOOT > 1 */
700	(void)close(b);
701#endif /* NUMBOOT > 0 */
702	/*
703	 * Make sure no part of the bootstrap is written in the area
704	 * reserved for the label.
705	 */
706	for (p = (char *)lp; p < (char *)lp + sizeof(struct disklabel); p++)
707		if (*p)
708			errx(2, "bootstrap doesn't leave room for disk label");
709	return (lp);
710}
711
712void
713display(f, lp)
714	FILE *f;
715	register struct disklabel *lp;
716{
717	register int i, j;
718	register struct partition *pp;
719
720	fprintf(f, "# %s:\n", specname);
721	if ((unsigned) lp->d_type < DKMAXTYPES)
722		fprintf(f, "type: %s\n", dktypenames[lp->d_type]);
723	else
724		fprintf(f, "type: %u\n", lp->d_type);
725	fprintf(f, "disk: %.*s\n", (int)sizeof(lp->d_typename),
726		lp->d_typename);
727	fprintf(f, "label: %.*s\n", (int)sizeof(lp->d_packname),
728		lp->d_packname);
729	fprintf(f, "flags:");
730	if (lp->d_flags & D_REMOVABLE)
731		fprintf(f, " removeable");
732	if (lp->d_flags & D_ECC)
733		fprintf(f, " ecc");
734	if (lp->d_flags & D_BADSECT)
735		fprintf(f, " badsect");
736	fprintf(f, "\n");
737	fprintf(f, "bytes/sector: %lu\n", (u_long)lp->d_secsize);
738	fprintf(f, "sectors/track: %lu\n", (u_long)lp->d_nsectors);
739	fprintf(f, "tracks/cylinder: %lu\n", (u_long)lp->d_ntracks);
740	fprintf(f, "sectors/cylinder: %lu\n", (u_long)lp->d_secpercyl);
741	fprintf(f, "cylinders: %lu\n", (u_long)lp->d_ncylinders);
742	fprintf(f, "sectors/unit: %lu\n", (u_long)lp->d_secperunit);
743	fprintf(f, "rpm: %u\n", lp->d_rpm);
744	fprintf(f, "interleave: %u\n", lp->d_interleave);
745	fprintf(f, "trackskew: %u\n", lp->d_trackskew);
746	fprintf(f, "cylinderskew: %u\n", lp->d_cylskew);
747	fprintf(f, "headswitch: %lu\t\t# milliseconds\n",
748	    (u_long)lp->d_headswitch);
749	fprintf(f, "track-to-track seek: %ld\t# milliseconds\n",
750	    (u_long)lp->d_trkseek);
751	fprintf(f, "drivedata: ");
752	for (i = NDDATA - 1; i >= 0; i--)
753		if (lp->d_drivedata[i])
754			break;
755	if (i < 0)
756		i = 0;
757	for (j = 0; j <= i; j++)
758		fprintf(f, "%lu ", (u_long)lp->d_drivedata[j]);
759	fprintf(f, "\n\n%u partitions:\n", lp->d_npartitions);
760	fprintf(f,
761	    "#        size   offset    fstype   [fsize bsize bps/cpg]\n");
762	pp = lp->d_partitions;
763	for (i = 0; i < lp->d_npartitions; i++, pp++) {
764		if (pp->p_size) {
765			fprintf(f, "  %c: %8lu %8lu  ", 'a' + i,
766			   (u_long)pp->p_size, (u_long)pp->p_offset);
767			if ((unsigned) pp->p_fstype < FSMAXTYPES)
768				fprintf(f, "%8.8s", fstypenames[pp->p_fstype]);
769			else
770				fprintf(f, "%8d", pp->p_fstype);
771			switch (pp->p_fstype) {
772
773			case FS_UNUSED:				/* XXX */
774				fprintf(f, "    %5lu %5lu %5.5s ",
775				    (u_long)pp->p_fsize,
776				    (u_long)(pp->p_fsize * pp->p_frag), "");
777				break;
778
779			case FS_BSDFFS:
780				fprintf(f, "    %5lu %5lu %5u ",
781				    (u_long)pp->p_fsize,
782				    (u_long)(pp->p_fsize * pp->p_frag),
783				    pp->p_cpg);
784				break;
785
786			case FS_BSDLFS:
787				fprintf(f, "    %5lu %5lu %5d",
788				    (u_long)pp->p_fsize,
789				    (u_long)(pp->p_fsize * pp->p_frag),
790				    pp->p_cpg);
791				break;
792
793			default:
794				fprintf(f, "%20.20s", "");
795				break;
796			}
797			fprintf(f, "\t# (Cyl. %4lu",
798			    (u_long)(pp->p_offset / lp->d_secpercyl));
799			if (pp->p_offset % lp->d_secpercyl)
800			    putc('*', f);
801			else
802			    putc(' ', f);
803			fprintf(f, "- %lu",
804			    (u_long)((pp->p_offset + pp->p_size +
805			    lp->d_secpercyl - 1) /
806			    lp->d_secpercyl - 1));
807			if (pp->p_size % lp->d_secpercyl)
808			    putc('*', f);
809			fprintf(f, ")\n");
810		}
811	}
812	fflush(f);
813}
814
815int
816edit(lp, f)
817	struct disklabel *lp;
818	int f;
819{
820	register int c, fd;
821	struct disklabel label;
822	FILE *fp;
823
824	if ((fd = mkstemp(tmpfil)) == -1 ||
825	    (fp = fdopen(fd, "w")) == NULL) {
826		warnx("can't create %s", tmpfil);
827		return (1);
828	}
829	display(fp, lp);
830	fclose(fp);
831	for (;;) {
832		if (!editit())
833			break;
834		fp = fopen(tmpfil, "r");
835		if (fp == NULL) {
836			warnx("can't reopen %s for reading", tmpfil);
837			break;
838		}
839		bzero((char *)&label, sizeof(label));
840		if (getasciilabel(fp, &label)) {
841			*lp = label;
842			if (writelabel(f, bootarea, lp) == 0) {
843				fclose(fp);
844				(void) unlink(tmpfil);
845				return (0);
846			}
847		}
848		fclose(fp);
849		printf("re-edit the label? [y]: "); fflush(stdout);
850		c = getchar();
851		if (c != EOF && c != (int)'\n')
852			while (getchar() != (int)'\n')
853				;
854		if  (c == (int)'n')
855			break;
856	}
857	(void) unlink(tmpfil);
858	return (1);
859}
860
861int
862editit()
863{
864	register int pid, xpid;
865	int stat, omask;
866
867	omask = sigblock(sigmask(SIGINT)|sigmask(SIGQUIT)|sigmask(SIGHUP));
868	while ((pid = fork()) < 0) {
869		extern int errno;
870
871		if (errno == EPROCLIM) {
872			warnx("you have too many processes");
873			return(0);
874		}
875		if (errno != EAGAIN) {
876			warn("fork");
877			return(0);
878		}
879		sleep(1);
880	}
881	if (pid == 0) {
882		register char *ed;
883
884		sigsetmask(omask);
885		setgid(getgid());
886		setuid(getuid());
887		if ((ed = getenv("EDITOR")) == (char *)0)
888			ed = DEFEDITOR;
889		execlp(ed, ed, tmpfil, 0);
890		err(1, "%s", ed);
891	}
892	while ((xpid = wait(&stat)) >= 0)
893		if (xpid == pid)
894			break;
895	sigsetmask(omask);
896	return(!stat);
897}
898
899char *
900skip(cp)
901	register char *cp;
902{
903
904	while (*cp != '\0' && isspace(*cp))
905		cp++;
906	if (*cp == '\0' || *cp == '#')
907		return ((char *)NULL);
908	return (cp);
909}
910
911char *
912word(cp)
913	register char *cp;
914{
915	register char c;
916
917	while (*cp != '\0' && !isspace(*cp) && *cp != '#')
918		cp++;
919	if ((c = *cp) != '\0') {
920		*cp++ = '\0';
921		if (c != '#')
922			return (skip(cp));
923	}
924	return ((char *)NULL);
925}
926
927/*
928 * Read an ascii label in from fd f,
929 * in the same format as that put out by display(),
930 * and fill in lp.
931 */
932int
933getasciilabel(f, lp)
934	FILE	*f;
935	register struct disklabel *lp;
936{
937	register char **cpp, *cp;
938	register struct partition *pp;
939	char *tp, *s, line[BUFSIZ];
940	int v, lineno = 0, errors = 0;
941
942	lp->d_bbsize = BBSIZE;				/* XXX */
943	lp->d_sbsize = SBSIZE;				/* XXX */
944	while (fgets(line, sizeof(line) - 1, f)) {
945		lineno++;
946		if ((cp = index(line,'\n')) != 0)
947			*cp = '\0';
948		cp = skip(line);
949		if (cp == NULL)
950			continue;
951		tp = index(cp, ':');
952		if (tp == NULL) {
953			fprintf(stderr, "line %d: syntax error\n", lineno);
954			errors++;
955			continue;
956		}
957		*tp++ = '\0', tp = skip(tp);
958		if (streq(cp, "type")) {
959			if (tp == NULL)
960				tp = "unknown";
961			cpp = dktypenames;
962			for (; cpp < &dktypenames[DKMAXTYPES]; cpp++)
963				if ((s = *cpp) && streq(s, tp)) {
964					lp->d_type = cpp - dktypenames;
965					goto next;
966				}
967			v = atoi(tp);
968			if ((unsigned)v >= DKMAXTYPES)
969				fprintf(stderr, "line %d:%s %d\n", lineno,
970				    "Warning, unknown disk type", v);
971			lp->d_type = v;
972			continue;
973		}
974		if (streq(cp, "flags")) {
975			for (v = 0; (cp = tp) && *cp != '\0';) {
976				tp = word(cp);
977				if (streq(cp, "removeable"))
978					v |= D_REMOVABLE;
979				else if (streq(cp, "ecc"))
980					v |= D_ECC;
981				else if (streq(cp, "badsect"))
982					v |= D_BADSECT;
983				else {
984					fprintf(stderr,
985					    "line %d: %s: bad flag\n",
986					    lineno, cp);
987					errors++;
988				}
989			}
990			lp->d_flags = v;
991			continue;
992		}
993		if (streq(cp, "drivedata")) {
994			register int i;
995
996			for (i = 0; (cp = tp) && *cp != '\0' && i < NDDATA;) {
997				lp->d_drivedata[i++] = atoi(cp);
998				tp = word(cp);
999			}
1000			continue;
1001		}
1002		if (sscanf(cp, "%d partitions", &v) == 1) {
1003			if (v == 0 || (unsigned)v > MAXPARTITIONS) {
1004				fprintf(stderr,
1005				    "line %d: bad # of partitions\n", lineno);
1006				lp->d_npartitions = MAXPARTITIONS;
1007				errors++;
1008			} else
1009				lp->d_npartitions = v;
1010			continue;
1011		}
1012		if (tp == NULL)
1013			tp = "";
1014		if (streq(cp, "disk")) {
1015			strncpy(lp->d_typename, tp, sizeof (lp->d_typename));
1016			continue;
1017		}
1018		if (streq(cp, "label")) {
1019			strncpy(lp->d_packname, tp, sizeof (lp->d_packname));
1020			continue;
1021		}
1022		if (streq(cp, "bytes/sector")) {
1023			v = atoi(tp);
1024			if (v <= 0 || (v % DEV_BSIZE) != 0) {
1025				fprintf(stderr,
1026				    "line %d: %s: bad sector size\n",
1027				    lineno, tp);
1028				errors++;
1029			} else
1030				lp->d_secsize = v;
1031			continue;
1032		}
1033		if (streq(cp, "sectors/track")) {
1034			v = atoi(tp);
1035			if (v <= 0) {
1036				fprintf(stderr, "line %d: %s: bad %s\n",
1037				    lineno, tp, cp);
1038				errors++;
1039			} else
1040				lp->d_nsectors = v;
1041			continue;
1042		}
1043		if (streq(cp, "sectors/cylinder")) {
1044			v = atoi(tp);
1045			if (v <= 0) {
1046				fprintf(stderr, "line %d: %s: bad %s\n",
1047				    lineno, tp, cp);
1048				errors++;
1049			} else
1050				lp->d_secpercyl = v;
1051			continue;
1052		}
1053		if (streq(cp, "tracks/cylinder")) {
1054			v = atoi(tp);
1055			if (v <= 0) {
1056				fprintf(stderr, "line %d: %s: bad %s\n",
1057				    lineno, tp, cp);
1058				errors++;
1059			} else
1060				lp->d_ntracks = v;
1061			continue;
1062		}
1063		if (streq(cp, "cylinders")) {
1064			v = atoi(tp);
1065			if (v <= 0) {
1066				fprintf(stderr, "line %d: %s: bad %s\n",
1067				    lineno, tp, cp);
1068				errors++;
1069			} else
1070				lp->d_ncylinders = v;
1071			continue;
1072		}
1073		if (streq(cp, "sectors/unit")) {
1074			v = atoi(tp);
1075			if (v <= 0) {
1076				fprintf(stderr, "line %d: %s: bad %s\n",
1077				    lineno, tp, cp);
1078				errors++;
1079			} else
1080				lp->d_secperunit = v;
1081			continue;
1082		}
1083		if (streq(cp, "rpm")) {
1084			v = atoi(tp);
1085			if (v <= 0) {
1086				fprintf(stderr, "line %d: %s: bad %s\n",
1087				    lineno, tp, cp);
1088				errors++;
1089			} else
1090				lp->d_rpm = v;
1091			continue;
1092		}
1093		if (streq(cp, "interleave")) {
1094			v = atoi(tp);
1095			if (v <= 0) {
1096				fprintf(stderr, "line %d: %s: bad %s\n",
1097				    lineno, tp, cp);
1098				errors++;
1099			} else
1100				lp->d_interleave = v;
1101			continue;
1102		}
1103		if (streq(cp, "trackskew")) {
1104			v = atoi(tp);
1105			if (v < 0) {
1106				fprintf(stderr, "line %d: %s: bad %s\n",
1107				    lineno, tp, cp);
1108				errors++;
1109			} else
1110				lp->d_trackskew = v;
1111			continue;
1112		}
1113		if (streq(cp, "cylinderskew")) {
1114			v = atoi(tp);
1115			if (v < 0) {
1116				fprintf(stderr, "line %d: %s: bad %s\n",
1117				    lineno, tp, cp);
1118				errors++;
1119			} else
1120				lp->d_cylskew = v;
1121			continue;
1122		}
1123		if (streq(cp, "headswitch")) {
1124			v = atoi(tp);
1125			if (v < 0) {
1126				fprintf(stderr, "line %d: %s: bad %s\n",
1127				    lineno, tp, cp);
1128				errors++;
1129			} else
1130				lp->d_headswitch = v;
1131			continue;
1132		}
1133		if (streq(cp, "track-to-track seek")) {
1134			v = atoi(tp);
1135			if (v < 0) {
1136				fprintf(stderr, "line %d: %s: bad %s\n",
1137				    lineno, tp, cp);
1138				errors++;
1139			} else
1140				lp->d_trkseek = v;
1141			continue;
1142		}
1143		if ('a' <= *cp && *cp <= 'z' && cp[1] == '\0') {
1144			unsigned part = *cp - 'a';
1145
1146			if (part > lp->d_npartitions) {
1147				fprintf(stderr,
1148				    "line %d: bad partition name\n", lineno);
1149				errors++;
1150				continue;
1151			}
1152			pp = &lp->d_partitions[part];
1153#define NXTNUM(n) { \
1154	if (tp == NULL) { \
1155		fprintf(stderr, "line %d: too few numeric fields\n", lineno); \
1156		errors++; \
1157		break; \
1158	} else { \
1159		cp = tp, tp = word(cp); \
1160		if (tp == NULL) \
1161			tp = cp; \
1162		(n) = atoi(cp); \
1163	} \
1164     }
1165
1166			NXTNUM(v);
1167			if (v < 0) {
1168				fprintf(stderr,
1169				    "line %d: %s: bad partition size\n",
1170				    lineno, cp);
1171				errors++;
1172			} else
1173				pp->p_size = v;
1174			NXTNUM(v);
1175			if (v < 0) {
1176				fprintf(stderr,
1177				    "line %d: %s: bad partition offset\n",
1178				    lineno, cp);
1179				errors++;
1180			} else
1181				pp->p_offset = v;
1182			cp = tp, tp = word(cp);
1183			cpp = fstypenames;
1184			for (; cpp < &fstypenames[FSMAXTYPES]; cpp++)
1185				if ((s = *cpp) && streq(s, cp)) {
1186					pp->p_fstype = cpp - fstypenames;
1187					goto gottype;
1188				}
1189			if (isdigit(*cp))
1190				v = atoi(cp);
1191			else
1192				v = FSMAXTYPES;
1193			if ((unsigned)v >= FSMAXTYPES) {
1194				fprintf(stderr, "line %d: %s %s\n", lineno,
1195				    "Warning, unknown filesystem type", cp);
1196				v = FS_UNUSED;
1197			}
1198			pp->p_fstype = v;
1199	gottype:
1200
1201			switch (pp->p_fstype) {
1202
1203			case FS_UNUSED:				/* XXX */
1204				NXTNUM(pp->p_fsize);
1205				if (pp->p_fsize == 0)
1206					break;
1207				NXTNUM(v);
1208				pp->p_frag = v / pp->p_fsize;
1209				break;
1210
1211			case FS_BSDFFS:
1212				NXTNUM(pp->p_fsize);
1213				if (pp->p_fsize == 0)
1214					break;
1215				NXTNUM(v);
1216				pp->p_frag = v / pp->p_fsize;
1217				NXTNUM(pp->p_cpg);
1218				break;
1219
1220			case FS_BSDLFS:
1221				NXTNUM(pp->p_fsize);
1222				if (pp->p_fsize == 0)
1223					break;
1224				NXTNUM(v);
1225				pp->p_frag = v / pp->p_fsize;
1226				NXTNUM(pp->p_cpg);
1227				break;
1228
1229			default:
1230				break;
1231			}
1232			continue;
1233		}
1234		fprintf(stderr, "line %d: %s: Unknown disklabel field\n",
1235		    lineno, cp);
1236		errors++;
1237	next:
1238		;
1239	}
1240	errors += checklabel(lp);
1241	return (errors == 0);
1242}
1243
1244/*
1245 * Check disklabel for errors and fill in
1246 * derived fields according to supplied values.
1247 */
1248int
1249checklabel(lp)
1250	register struct disklabel *lp;
1251{
1252	register struct partition *pp;
1253	int i, errors = 0;
1254	char part;
1255
1256	if (lp->d_secsize == 0) {
1257		fprintf(stderr, "sector size 0\n");
1258		return (1);
1259	}
1260	if (lp->d_nsectors == 0) {
1261		fprintf(stderr, "sectors/track 0\n");
1262		return (1);
1263	}
1264	if (lp->d_ntracks == 0) {
1265		fprintf(stderr, "tracks/cylinder 0\n");
1266		return (1);
1267	}
1268	if  (lp->d_ncylinders == 0) {
1269		fprintf(stderr, "cylinders/unit 0\n");
1270		errors++;
1271	}
1272	if (lp->d_rpm == 0)
1273		Warning("revolutions/minute 0");
1274	if (lp->d_secpercyl == 0)
1275		lp->d_secpercyl = lp->d_nsectors * lp->d_ntracks;
1276	if (lp->d_secperunit == 0)
1277		lp->d_secperunit = lp->d_secpercyl * lp->d_ncylinders;
1278	if (lp->d_bbsize == 0) {
1279		fprintf(stderr, "boot block size 0\n");
1280		errors++;
1281	} else if (lp->d_bbsize % lp->d_secsize)
1282		Warning("boot block size %% sector-size != 0");
1283	if (lp->d_sbsize == 0) {
1284		fprintf(stderr, "super block size 0\n");
1285		errors++;
1286	} else if (lp->d_sbsize % lp->d_secsize)
1287		Warning("super block size %% sector-size != 0");
1288	if (lp->d_npartitions > MAXPARTITIONS)
1289		Warning("number of partitions (%lu) > MAXPARTITIONS (%d)",
1290		    (u_long)lp->d_npartitions, MAXPARTITIONS);
1291	for (i = 0; i < lp->d_npartitions; i++) {
1292		part = 'a' + i;
1293		pp = &lp->d_partitions[i];
1294		if (pp->p_size == 0 && pp->p_offset != 0)
1295			Warning("partition %c: size 0, but offset %lu",
1296			    part, (u_long)pp->p_offset);
1297#ifdef notdef
1298		if (pp->p_size % lp->d_secpercyl)
1299			Warning("partition %c: size %% cylinder-size != 0",
1300			    part);
1301		if (pp->p_offset % lp->d_secpercyl)
1302			Warning("partition %c: offset %% cylinder-size != 0",
1303			    part);
1304#endif
1305		if (pp->p_offset > lp->d_secperunit) {
1306			fprintf(stderr,
1307			    "partition %c: offset past end of unit\n", part);
1308			errors++;
1309		}
1310		if (pp->p_offset + pp->p_size > lp->d_secperunit) {
1311			fprintf(stderr,
1312			"partition %c: partition extends past end of unit\n",
1313			    part);
1314			errors++;
1315		}
1316	}
1317	for (; i < MAXPARTITIONS; i++) {
1318		part = 'a' + i;
1319		pp = &lp->d_partitions[i];
1320		if (pp->p_size || pp->p_offset)
1321			Warning("unused partition %c: size %d offset %lu",
1322			    'a' + i, pp->p_size, (u_long)pp->p_offset);
1323	}
1324	return (errors);
1325}
1326
1327/*
1328 * When operating on a "virgin" disk, try getting an initial label
1329 * from the associated device driver.  This might work for all device
1330 * drivers that are able to fetch some initial device parameters
1331 * without even having access to a (BSD) disklabel, like SCSI disks,
1332 * most IDE drives, or vn devices.
1333 *
1334 * The device name must be given in its "canonical" form.
1335 */
1336struct disklabel *
1337getvirginlabel(void)
1338{
1339	static struct disklabel lab;
1340	char namebuf[BBSIZE];
1341	int f;
1342
1343	if (dkname[0] == '/') {
1344		warnx("\"auto\" requires the usage of a canonical disk name");
1345		return (NULL);
1346	}
1347	(void)snprintf(namebuf, BBSIZE, "%sr%s", _PATH_DEV, dkname);
1348	if ((f = open(namebuf, O_RDONLY)) == -1) {
1349		warn("cannot open %s", namebuf);
1350		return (NULL);
1351	}
1352	if (ioctl(f, DIOCGDINFO, &lab) < 0) {
1353		warn("ioctl DIOCGDINFO");
1354		close(f);
1355		return (NULL);
1356	}
1357	close(f);
1358	lab.d_boot0 = NULL;
1359	lab.d_boot1 = NULL;
1360	return (&lab);
1361}
1362
1363/*
1364 * If we are installing a boot program that doesn't fit in d_bbsize
1365 * we need to mark those partitions that the boot overflows into.
1366 * This allows newfs to prevent creation of a filesystem where it might
1367 * clobber bootstrap code.
1368 */
1369void
1370setbootflag(lp)
1371	register struct disklabel *lp;
1372{
1373	register struct partition *pp;
1374	int i, errors = 0;
1375	char part;
1376	u_long boffset;
1377
1378	if (bootbuf == 0)
1379		return;
1380	boffset = bootsize / lp->d_secsize;
1381	for (i = 0; i < lp->d_npartitions; i++) {
1382		part = 'a' + i;
1383		pp = &lp->d_partitions[i];
1384		if (pp->p_size == 0)
1385			continue;
1386		if (boffset <= pp->p_offset) {
1387			if (pp->p_fstype == FS_BOOT)
1388				pp->p_fstype = FS_UNUSED;
1389		} else if (pp->p_fstype != FS_BOOT) {
1390			if (pp->p_fstype != FS_UNUSED) {
1391				fprintf(stderr,
1392					"boot overlaps used partition %c\n",
1393					part);
1394				errors++;
1395			} else {
1396				pp->p_fstype = FS_BOOT;
1397				Warning("boot overlaps partition %c, %s",
1398					part, "marked as FS_BOOT");
1399			}
1400		}
1401	}
1402	if (errors)
1403		errx(4, "cannot install boot program");
1404}
1405
1406/*VARARGS1*/
1407void
1408Warning(char *fmt, ...)
1409{
1410	va_list ap;
1411
1412	fprintf(stderr, "Warning, ");
1413	va_start(ap, fmt);
1414	vfprintf(stderr, fmt, ap);
1415	fprintf(stderr, "\n");
1416	va_end(ap);
1417}
1418
1419void
1420usage()
1421{
1422#if NUMBOOT > 0
1423	fprintf(stderr, "%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%s\n%s\n",
1424		"usage: disklabel [-r] disk",
1425		"\t\t(to read label)",
1426		"       disklabel -w [-r] disk type [ packid ]",
1427		"\t\t(to write label with existing boot program)",
1428		"       disklabel -e [-r] disk",
1429		"\t\t(to edit label)",
1430		"       disklabel -R [-r] disk protofile",
1431		"\t\t(to restore label with existing boot program)",
1432#if NUMBOOT > 1
1433		"       disklabel -B [ -b boot1 [ -s boot2 ] ] disk [ type ]",
1434		"\t\t(to install boot program with existing label)",
1435		"       disklabel -w -B [ -b boot1 [ -s boot2 ] ] disk type [ packid ]",
1436		"\t\t(to write label and boot program)",
1437		"       disklabel -R -B [ -b boot1 [ -s boot2 ] ] disk protofile [ type ]",
1438		"\t\t(to restore label and boot program)",
1439#else
1440		"       disklabel -B [ -b bootprog ] disk [ type ]",
1441		"\t\t(to install boot program with existing on-disk label)",
1442		"       disklabel -w -B [ -b bootprog ] disk type [ packid ]",
1443		"\t\t(to write label and install boot program)",
1444		"       disklabel -R -B [ -b bootprog ] disk protofile [ type ]",
1445		"\t\t(to restore label and install boot program)",
1446#endif
1447		"       disklabel [-NW] disk",
1448		"\t\t(to write disable/enable label)");
1449#else
1450	fprintf(stderr, "%s\n%s\n%s\n%s\n%s\n%s\n%s\n%s\n%s\n",
1451		"usage: disklabel [-r] disk", "(to read label)",
1452		"       disklabel -w [-r] disk type [ packid ]",
1453		"\t\t(to write label)",
1454		"       disklabel -e [-r] disk",
1455		"\t\t(to edit label)",
1456		"       disklabel -R [-r] disk protofile",
1457		"\t\t(to restore label)",
1458		"       disklabel [-NW] disk",
1459		"\t\t(to write disable/enable label)");
1460#endif
1461	exit(1);
1462}
1463