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