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