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