xref: /csrg-svn/sbin/disklabel/disklabel.c (revision 34032)
1 /*
2  * Copyright (c) 1987 Regents of the University of California.
3  * All rights reserved.  The Berkeley software License Agreement
4  * specifies the terms and conditions for redistribution.
5  */
6 
7 #ifndef lint
8 static char sccsid[] = "@(#)disklabel.c	5.12 (Berkeley) 04/20/88";
9 /* from static char sccsid[] = "@(#)disklabel.c	1.2 (Symmetric) 11/28/85"; */
10 #endif
11 
12 #include <stdio.h>
13 #include <ctype.h>
14 #include <sys/param.h>
15 #include <sys/signal.h>
16 #include <sys/errno.h>
17 #include <sys/file.h>
18 #include <sys/ioctl.h>
19 #include <sys/fs.h>
20 #include <strings.h>
21 #define DKTYPENAMES
22 #include <sys/disklabel.h>
23 
24 /*
25  * Disklabel: read and write disklabels.
26  * The label is usually placed on one of the first sectors of the disk.
27  * Many machines (VAX 11/750) also place a bootstrap in the same area,
28  * in which case the label is embedded in the bootstrap.
29  * The bootstrap source must leave space at the proper offset
30  * for the label on such machines.
31  */
32 
33 #ifdef vax
34 #define RAWPARTITION	'c'
35 #else
36 #define RAWPARTITION	'a'
37 #endif
38 
39 #ifndef BBSIZE
40 #define	BBSIZE	8192			/* size of boot area, with label */
41 #endif
42 
43 #ifdef vax
44 #define	BOOT				/* also have bootstrap in "boot area" */
45 #define	BOOTDIR	"/usr/mdec"		/* source of boot binaries */
46 #else
47 #ifdef lint
48 #define	BOOT
49 #endif
50 #endif
51 
52 #define	DEFEDITOR	"/usr/ucb/vi"
53 #define	streq(a,b)	(strcmp(a,b) == 0)
54 
55 #ifdef BOOT
56 char	*xxboot;
57 char	*bootxx;
58 #endif
59 
60 char	*dkname;
61 char	*specname;
62 char	tmpfil[] = "/tmp/EdDk.aXXXXXX";
63 
64 extern	int errno;
65 char	namebuf[BBSIZE], *np = namebuf;
66 struct	disklabel lab;
67 struct	disklabel *readlabel(), *makebootarea();
68 char	bootarea[BBSIZE];
69 char	boot0[MAXPATHLEN];
70 char	boot1[MAXPATHLEN];
71 
72 enum	{ UNSPEC, EDIT, NOWRITE, READ, RESTORE, WRITE, WRITEABLE } op = UNSPEC;
73 
74 int	rflag;
75 
76 #ifdef DEBUG
77 int	debug;
78 #endif
79 
80 main(argc, argv)
81 	int argc;
82 	char *argv[];
83 {
84 	extern int optind;
85 	register struct disklabel *lp;
86 	FILE *t;
87 	int ch, f;
88 	char *name = 0, *type;
89 
90 	while ((ch = getopt(argc, argv, "NRWerw")) != EOF)
91 		switch (ch) {
92 			case 'N':
93 				if (op != UNSPEC)
94 					usage();
95 				op = NOWRITE;
96 				break;
97 			case 'R':
98 				if (op != UNSPEC)
99 					usage();
100 				op = RESTORE;
101 				break;
102 			case 'W':
103 				if (op != UNSPEC)
104 					usage();
105 				op = WRITEABLE;
106 				break;
107 			case 'e':
108 				if (op != UNSPEC)
109 					usage();
110 				op = EDIT;
111 				break;
112 			case 'r':
113 				++rflag;
114 				break;
115 			case 'w':
116 				if (op != UNSPEC)
117 					usage();
118 				op = WRITE;
119 				break;
120 #ifdef DEBUG
121 			case 'd':
122 				debug++;
123 				break;
124 #endif
125 			case '?':
126 			default:
127 				usage();
128 		}
129 	argc -= optind;
130 	argv += optind;
131 	if (op == UNSPEC)
132 		op = READ;
133 	if (argc < 1)
134 		usage();
135 
136 	dkname = argv[0];
137 	if (dkname[0] != '/') {
138 		(void)sprintf(np, "/dev/r%s%c", dkname, RAWPARTITION);
139 		specname = np;
140 		np += strlen(specname) + 1;
141 	} else
142 		specname = dkname;
143 	f = open(specname, op == READ ? O_RDONLY : O_RDWR);
144 	if (f < 0 && errno == ENOENT && dkname[0] != '/') {
145 		(void)sprintf(specname, "/dev/r%s", dkname);
146 		np = namebuf + strlen(specname) + 1;
147 		f = open(specname, op == READ ? O_RDONLY : O_RDWR);
148 	}
149 	if (f < 0)
150 		Perror(specname);
151 
152 	switch(op) {
153 	case EDIT:
154 		if (argc != 1)
155 			usage();
156 		lp = readlabel(f);
157 		if (edit(lp))
158 			writelabel(f, bootarea, lp);
159 		break;
160 	case NOWRITE: {
161 		int flag = 0;
162 		if (ioctl(f, DIOCWLABEL, (char *)&flag) < 0)
163 			Perror("ioctl DIOCWLABEL");
164 		break;
165 	}
166 	case READ:
167 		if (argc != 1)
168 			usage();
169 		lp = readlabel(f);
170 		display(stdout, lp);
171 		(void) checklabel(lp);
172 		break;
173 	case RESTORE:
174 #ifdef BOOT
175 		if (rflag) {
176 			if (argc == 4) {	/* [ priboot secboot ] */
177 				xxboot = argv[2];
178 				bootxx = argv[3];
179 				lab.d_secsize = DEV_BSIZE;	/* XXX */
180 				lab.d_bbsize = BBSIZE;		/* XXX */
181 			}
182 			else if (argc == 3) 	/* [ disktype ] */
183 				makelabel(argv[2], (char *)NULL, &lab);
184 			else {
185 				fprintf(stderr,
186 "Must specify either disktype or bootfiles with -r flag of RESTORE option\n");
187 				exit(1);
188 			}
189 		}
190 		else
191 #endif
192 		if (argc != 2)
193 			usage();
194 		lp = makebootarea(bootarea, &lab);
195 		if (!(t = fopen(argv[1],"r")))
196 			Perror(argv[1]);
197 		if (getasciilabel(t, lp))
198 			writelabel(f, bootarea, lp);
199 		break;
200 	case WRITE:
201 		type = argv[1];
202 #ifdef BOOT
203 		if (argc > 5 || argc < 2)
204 			usage();
205 		if (argc > 3) {
206 			bootxx = argv[--argc];
207 			xxboot = argv[--argc];
208 		}
209 #else
210 		if (argc > 3 || argc < 2)
211 			usage();
212 #endif
213 		if (argc > 2)
214 			name = argv[--argc];
215 		makelabel(type, name, &lab);
216 		lp = makebootarea(bootarea, &lab);
217 		*lp = lab;
218 		if (checklabel(lp) == 0)
219 			writelabel(f, bootarea, lp);
220 		break;
221 	case WRITEABLE: {
222 		int flag = 1;
223 		if (ioctl(f, DIOCWLABEL, (char *)&flag) < 0)
224 			Perror("ioctl DIOCWLABEL");
225 		break;
226 	}
227 	}
228 	exit(0);
229 }
230 
231 /*
232  * Construct a prototype disklabel from /etc/disktab.  As a side
233  * effect, set the names of the primary and secondary boot files
234  * if specified.
235  */
236 makelabel(type, name, lp)
237 	char *type, *name;
238 	register struct disklabel *lp;
239 {
240 	register struct disklabel *dp;
241 	char *strcpy();
242 
243 	dp = getdiskbyname(type);
244 	if (dp == NULL) {
245 		fprintf(stderr, "%s: unknown disk type\n", type);
246 		exit(1);
247 	}
248 	*lp = *dp;
249 #ifdef BOOT
250 	/*
251 	 * Check if disktab specifies the bootstraps (b0 or b1).
252 	 */
253 	if (!xxboot && lp->d_boot0) {
254 		if (*lp->d_boot0 != '/')
255 			(void)sprintf(boot0, "%s/%s", BOOTDIR, lp->d_boot0);
256 		else
257 			(void)strcpy(boot0, lp->d_boot0);
258 		xxboot = boot0;
259 	}
260 	if (!bootxx && lp->d_boot1) {
261 		if (*lp->d_boot1 != '/')
262 			(void)sprintf(boot1, "%s/%s", BOOTDIR, lp->d_boot1);
263 		else
264 			(void)strcpy(boot1, lp->d_boot1);
265 		bootxx = boot1;
266 	}
267 	/*
268 	 * If bootstraps not specified anywhere, makebootarea()
269 	 * will choose ones based on the name of the disk special
270 	 * file. E.g. /dev/ra0 -> raboot, bootra
271 	 */
272 #endif /*BOOT*/
273 	/* d_packname is union d_boot[01], so zero */
274 	bzero(lp->d_packname, sizeof(lp->d_packname));
275 	if (name)
276 		(void)strncpy(lp->d_packname, name, sizeof(lp->d_packname));
277 }
278 
279 writelabel(f, boot, lp)
280 	int f;
281 	char *boot;
282 	register struct disklabel *lp;
283 {
284 	register int i;
285 	int flag;
286 	off_t lseek();
287 
288 	lp->d_magic = DISKMAGIC;
289 	lp->d_magic2 = DISKMAGIC;
290 	lp->d_checksum = 0;
291 	lp->d_checksum = dkcksum(lp);
292 	if (rflag) {
293 		/*
294 		 * First set the kernel disk label,
295 		 * then write a label to the raw disk.
296 		 * If the SDINFO ioctl fails because it is unimplemented,
297 		 * keep going; otherwise, the kernel consistency checks
298 		 * may prevent us from changing the current (in-core)
299 		 * label.
300 		 */
301 		if (ioctl(f, DIOCSDINFO, lp) < 0 &&
302 		    errno != ENODEV && errno != ENOTTY)
303 			Perror("ioctl DIOCSDINFO");
304 		(void)lseek(f, (off_t)0, L_SET);
305 		/*
306 		 * write enable label sector before write (if necessary),
307 		 * disable after writing.
308 		 */
309 		flag = 1;
310 		if (ioctl(f, DIOCWLABEL, &flag) < 0)
311 			perror("ioctl DIOCWLABEL");
312 		if (write(f, boot, lp->d_bbsize) != lp->d_bbsize)
313 			Perror("write");
314 		flag = 0;
315 		(void) ioctl(f, DIOCWLABEL, &flag);
316 	} else if (ioctl(f, DIOCWDINFO, lp) < 0)
317 		Perror("ioctl DIOCWDINFO");
318 #ifdef vax
319 	if (lp->d_type == DTYPE_SMD && lp->d_flags & D_BADSECT) {
320 		daddr_t alt;
321 
322 		alt = lp->d_ncylinders * lp->d_secpercyl - lp->d_nsectors;
323 		for (i = 1; i < 11 && i < lp->d_nsectors; i += 2) {
324 			(void)lseek(f, (off_t)((alt + i) * lp->d_secsize), L_SET);
325 			if (write(f, boot, lp->d_secsize) < lp->d_secsize) {
326 				int oerrno = errno;
327 				fprintf(stderr, "alternate label %d ", i/2);
328 				errno = oerrno;
329 				perror("write");
330 			}
331 		}
332 	}
333 #endif
334 }
335 
336 /*
337  * Fetch disklabel for disk.
338  * Use ioctl to get label unless -r flag is given.
339  */
340 struct disklabel *
341 readlabel(f)
342 	int f;
343 {
344 	register struct disklabel *lp;
345 
346 	if (rflag) {
347 		if (read(f, bootarea, BBSIZE) < BBSIZE)
348 			Perror(specname);
349 		for (lp = (struct disklabel *)bootarea;
350 		    lp <= (struct disklabel *)(bootarea + BBSIZE - sizeof(*lp));
351 		    lp = (struct disklabel *)((char *)lp + 16))
352 			if (lp->d_magic == DISKMAGIC &&
353 			    lp->d_magic2 == DISKMAGIC)
354 				break;
355 		if (lp > (struct disklabel *)(bootarea+BBSIZE-sizeof(*lp)) ||
356 		    lp->d_magic != DISKMAGIC || lp->d_magic2 != DISKMAGIC ||
357 		    dkcksum(lp) != 0) {
358 			fprintf(stderr,
359 	"Bad pack magic number (label is damaged, or pack is unlabeled)\n");
360 			/* lp = (struct disklabel *)(bootarea + LABELOFFSET); */
361 			exit (1);
362 		}
363 	} else {
364 		lp = &lab;
365 		if (ioctl(f, DIOCGDINFO, lp) < 0)
366 			Perror("ioctl DIOCGDINFO");
367 	}
368 	return (lp);
369 }
370 
371 struct disklabel *
372 makebootarea(boot, dp)
373 	char *boot;
374 	register struct disklabel *dp;
375 {
376 	struct disklabel *lp;
377 	register char *p;
378 	int b;
379 #ifdef BOOT
380 	char	*dkbasename;
381 #endif /*BOOT*/
382 
383 	lp = (struct disklabel *)(boot + (LABELSECTOR * dp->d_secsize) +
384 	    LABELOFFSET);
385 #ifdef BOOT
386 	if (!rflag)
387 		return (lp);
388 
389 	if (xxboot == NULL || bootxx == NULL) {
390 		dkbasename = np;
391 		if ((p = rindex(dkname, '/')) == NULL)
392 			p = dkname;
393 		else
394 			p++;
395 		while (*p && !isdigit(*p))
396 			*np++ = *p++;
397 		*np++ = '\0';
398 
399 		if (xxboot == NULL) {
400 			(void)sprintf(np, "%s/%sboot", BOOTDIR, dkbasename);
401 			if (access(np, F_OK) < 0 && dkbasename[0] == 'r')
402 				dkbasename++;
403 			xxboot = np;
404 			(void)sprintf(xxboot, "%s/%sboot", BOOTDIR, dkbasename);
405 			np += strlen(xxboot) + 1;
406 		}
407 		if (bootxx == NULL) {
408 			(void)sprintf(np, "%s/boot%s", BOOTDIR, dkbasename);
409 			if (access(np, F_OK) < 0 && dkbasename[0] == 'r')
410 				dkbasename++;
411 			bootxx = np;
412 			(void)sprintf(bootxx, "%s/boot%s", BOOTDIR, dkbasename);
413 			np += strlen(bootxx) + 1;
414 		}
415 	}
416 #ifdef DEBUG
417 	if (debug)
418 		fprintf(stderr, "bootstraps: xxboot = %s, bootxx = %s\n",
419 			xxboot, bootxx);
420 #endif
421 
422 	b = open(xxboot, O_RDONLY);
423 	if (b < 0)
424 		Perror(xxboot);
425 	if (read(b, boot, (int)dp->d_secsize) < 0)
426 		Perror(xxboot);
427 	close(b);
428 	b = open(bootxx, O_RDONLY);
429 	if (b < 0)
430 		Perror(bootxx);
431 	if (read(b, &boot[dp->d_secsize], (int)(dp->d_bbsize-dp->d_secsize)) < 0)
432 		Perror(bootxx);
433 	(void)close(b);
434 #endif /*BOOT*/
435 
436 	for (p = (char *)lp; p < (char *)lp + sizeof(struct disklabel); p++)
437 		if (*p) {
438 			fprintf(stderr,
439 			    "Bootstrap doesn't leave room for disk label\n");
440 			exit(2);
441 		}
442 	return (lp);
443 }
444 
445 display(f, lp)
446 	FILE *f;
447 	register struct disklabel *lp;
448 {
449 	register int i, j;
450 	register struct partition *pp;
451 
452 	fprintf(f, "# %s:\n", specname);
453 	if ((unsigned) lp->d_type < DKMAXTYPES)
454 		fprintf(f, "type: %s\n", dktypenames[lp->d_type]);
455 	else
456 		fprintf(f, "type: %d\n", lp->d_type);
457 	fprintf(f, "disk: %.*s\n", sizeof(lp->d_typename), lp->d_typename);
458 	fprintf(f, "label: %.*s\n", sizeof(lp->d_packname), lp->d_packname);
459 	fprintf(f, "flags:");
460 	if (lp->d_flags & D_REMOVABLE)
461 		fprintf(f, " removeable");
462 	if (lp->d_flags & D_ECC)
463 		fprintf(f, " ecc");
464 	if (lp->d_flags & D_BADSECT)
465 		fprintf(f, " badsect");
466 	fprintf(f, "\n");
467 	fprintf(f, "bytes/sector: %d\n", lp->d_secsize);
468 	fprintf(f, "sectors/track: %d\n", lp->d_nsectors);
469 	fprintf(f, "tracks/cylinder: %d\n", lp->d_ntracks);
470 	fprintf(f, "sectors/cylinder: %d\n", lp->d_secpercyl);
471 	fprintf(f, "cylinders: %d\n", lp->d_ncylinders);
472 	fprintf(f, "rpm: %d\n", lp->d_rpm);
473 	fprintf(f, "interleave: %d\n", lp->d_interleave);
474 	fprintf(f, "trackskew: %d\n", lp->d_trackskew);
475 	fprintf(f, "cylinderskew: %d\n", lp->d_cylskew);
476 	fprintf(f, "headswitch: %d\t\t# milliseconds\n", lp->d_headswitch);
477 	fprintf(f, "track-to-track seek: %d\t# milliseconds\n", lp->d_trkseek);
478 	fprintf(f, "drivedata: ");
479 	for (i = NDDATA - 1; i >= 0; i--)
480 		if (lp->d_drivedata[i])
481 			break;
482 	if (i < 0)
483 		i = 0;
484 	for (j = 0; j <= i; j++)
485 		fprintf(f, "%d ", lp->d_drivedata[j]);
486 	fprintf(f, "\n\n%d partitions:\n", lp->d_npartitions);
487 	fprintf(f,
488 	    "#        size   offset    fstype   [fsize bsize   cpg]\n");
489 	pp = lp->d_partitions;
490 	for (i = 0; i < lp->d_npartitions; i++, pp++) {
491 		if (pp->p_size) {
492 			fprintf(f, "  %c: %8d %8d  ", 'a' + i,
493 			   pp->p_size, pp->p_offset);
494 			if ((unsigned) pp->p_fstype < FSMAXTYPES)
495 				fprintf(f, "%8.8s", fstypenames[pp->p_fstype]);
496 			else
497 				fprintf(f, "%8d", pp->p_fstype);
498 			switch (pp->p_fstype) {
499 
500 			case FS_UNUSED:				/* XXX */
501 				fprintf(f, "    %5d %5d %5.5s ",
502 				    pp->p_fsize, pp->p_fsize * pp->p_frag, "");
503 				break;
504 
505 			case FS_BSDFFS:
506 				fprintf(f, "    %5d %5d %5d ",
507 				    pp->p_fsize, pp->p_fsize * pp->p_frag,
508 				    pp->p_cpg);
509 				break;
510 
511 			default:
512 				fprintf(f, "%20.20s", "");
513 				break;
514 			}
515 			fprintf(f, "\t# (Cyl. %4d",
516 			    pp->p_offset / lp->d_secpercyl);
517 			if (pp->p_offset % lp->d_secpercyl)
518 			    putc('*', f);
519 			else
520 			    putc(' ', f);
521 			fprintf(f, "- %d",
522 			    (pp->p_offset +
523 			    pp->p_size + lp->d_secpercyl - 1) /
524 			    lp->d_secpercyl - 1);
525 			if (pp->p_size % lp->d_secpercyl)
526 			    putc('*', f);
527 			fprintf(f, ")\n");
528 		}
529 	}
530 	fflush(f);
531 }
532 
533 edit(lp)
534 	struct disklabel *lp;
535 {
536 	register int c;
537 	struct disklabel label;
538 	FILE *fd;
539 	char *mktemp();
540 
541 	(void) mktemp(tmpfil);
542 	fd = fopen(tmpfil, "w");
543 	if (fd == NULL) {
544 		fprintf(stderr, "%s: Can't create\n", tmpfil);
545 		return (0);
546 	}
547 	(void)fchmod(fd, 0600);
548 	display(fd, lp);
549 	fclose(fd);
550 	for (;;) {
551 		if (!editit())
552 			break;
553 		fd = fopen(tmpfil, "r");
554 		if (fd == NULL) {
555 			fprintf(stderr, "%s: Can't reopen for reading\n",
556 				tmpfil);
557 			break;
558 		}
559 		bzero((char *)&label, sizeof(label));
560 		if (getasciilabel(fd, &label)) {
561 			*lp = label;
562 			(void) unlink(tmpfil);
563 			return (1);
564 		}
565 		printf("re-edit the label? [y]: "); fflush(stdout);
566 		c = getchar();
567 		if (c != EOF && c != (int)'\n')
568 			while (getchar() != (int)'\n')
569 				;
570 		if  (c == (int)'n')
571 			break;
572 	}
573 	(void) unlink(tmpfil);
574 	return (0);
575 }
576 
577 editit()
578 {
579 	register int pid, xpid;
580 	int stat, omask;
581 	extern char *getenv();
582 
583 	omask = sigblock(sigmask(SIGINT)|sigmask(SIGQUIT)|sigmask(SIGHUP));
584 	while ((pid = fork()) < 0) {
585 		extern int errno;
586 
587 		if (errno == EPROCLIM) {
588 			fprintf(stderr, "You have too many processes\n");
589 			return(0);
590 		}
591 		if (errno != EAGAIN) {
592 			perror("fork");
593 			return(0);
594 		}
595 		sleep(1);
596 	}
597 	if (pid == 0) {
598 		register char *ed;
599 
600 		sigsetmask(omask);
601 		setgid(getgid());
602 		setuid(getuid());
603 		if ((ed = getenv("EDITOR")) == (char *)0)
604 			ed = DEFEDITOR;
605 		execlp(ed, ed, tmpfil, 0);
606 		perror(ed);
607 		exit(1);
608 	}
609 	while ((xpid = wait(&stat)) >= 0)
610 		if (xpid == pid)
611 			break;
612 	sigsetmask(omask);
613 	return(!stat);
614 }
615 
616 char *
617 skip(cp)
618 	register char *cp;
619 {
620 
621 	while (*cp != '\0' && isspace(*cp))
622 		cp++;
623 	if (*cp == '\0' || *cp == '#')
624 		return ((char *)NULL);
625 	return (cp);
626 }
627 
628 char *
629 word(cp)
630 	register char *cp;
631 {
632 	register char c;
633 
634 	while (*cp != '\0' && !isspace(*cp) && *cp != '#')
635 		cp++;
636 	if ((c = *cp) != '\0') {
637 		*cp++ = '\0';
638 		if (c != '#')
639 			return (skip(cp));
640 	}
641 	return ((char *)NULL);
642 }
643 
644 /*
645  * Read an ascii label in from fd f,
646  * in the same format as that put out by display(),
647  * and fill in lp.
648  */
649 getasciilabel(f, lp)
650 	FILE	*f;
651 	register struct disklabel *lp;
652 {
653 	register char **cpp, *cp;
654 	register struct partition *pp;
655 	char *tp, *s, line[BUFSIZ];
656 	int v, lineno = 0, errors = 0;
657 
658 	lp->d_bbsize = BBSIZE;				/* XXX */
659 	lp->d_sbsize = SBSIZE;				/* XXX */
660 	while (fgets(line, sizeof(line) - 1, f)) {
661 		lineno++;
662 		if (cp = index(line,'\n'))
663 			*cp = '\0';
664 		cp = skip(line);
665 		if (cp == NULL)
666 			continue;
667 		tp = index(cp, ':');
668 		if (tp == NULL) {
669 			fprintf(stderr, "line %d: syntax error\n", lineno);
670 			errors++;
671 			continue;
672 		}
673 		*tp++ = '\0', tp = skip(tp);
674 		if (streq(cp, "type")) {
675 			if (tp == NULL)
676 				tp = "unknown";
677 			cpp = dktypenames;
678 			for (; cpp < &dktypenames[DKMAXTYPES]; cpp++)
679 				if ((s = *cpp) && streq(s, tp)) {
680 					lp->d_type = cpp - dktypenames;
681 					goto next;
682 				}
683 			v = atoi(tp);
684 			if ((unsigned)v >= DKMAXTYPES)
685 				fprintf(stderr, "line %d:%s %d\n", lineno,
686 				    "Warning, unknown disk type", v);
687 			lp->d_type = v;
688 			continue;
689 		}
690 		if (streq(cp, "flags")) {
691 			for (v = 0; (cp = tp) && *cp != '\0';) {
692 				tp = word(cp);
693 				if (streq(cp, "removeable"))
694 					v |= D_REMOVABLE;
695 				else if (streq(cp, "ecc"))
696 					v |= D_ECC;
697 				else if (streq(cp, "badsect"))
698 					v |= D_BADSECT;
699 				else {
700 					fprintf(stderr,
701 					    "line %d: %s: bad flag\n",
702 					    lineno, cp);
703 					errors++;
704 				}
705 			}
706 			lp->d_flags = v;
707 			continue;
708 		}
709 		if (streq(cp, "drivedata")) {
710 			register int i;
711 
712 			for (i = 0; (cp = tp) && *cp != '\0' && i < NDDATA;) {
713 				lp->d_drivedata[i++] = atoi(cp);
714 				tp = word(cp);
715 			}
716 			continue;
717 		}
718 		if (sscanf(cp, "%d partitions", &v) == 1) {
719 			if (v == 0 || (unsigned)v > MAXPARTITIONS) {
720 				fprintf(stderr,
721 				    "line %d: bad # of partitions\n", lineno);
722 				lp->d_npartitions = MAXPARTITIONS;
723 				errors++;
724 			} else
725 				lp->d_npartitions = v;
726 			continue;
727 		}
728 		if (tp == NULL)
729 			tp = "";
730 		if (streq(cp, "disk")) {
731 			strncpy(lp->d_typename, tp, sizeof (lp->d_typename));
732 			continue;
733 		}
734 		if (streq(cp, "label")) {
735 			strncpy(lp->d_packname, tp, sizeof (lp->d_packname));
736 			continue;
737 		}
738 		if (streq(cp, "bytes/sector")) {
739 			v = atoi(tp);
740 			if (v <= 0 || (v % 512) != 0) {
741 				fprintf(stderr,
742 				    "line %d: %s: bad sector size\n",
743 				    lineno, tp);
744 				errors++;
745 			} else
746 				lp->d_secsize = v;
747 			continue;
748 		}
749 		if (streq(cp, "sectors/track")) {
750 			v = atoi(tp);
751 			if (v <= 0) {
752 				fprintf(stderr, "line %d: %s: bad %s\n",
753 				    lineno, tp, cp);
754 				errors++;
755 			} else
756 				lp->d_nsectors = v;
757 			continue;
758 		}
759 		if (streq(cp, "sectors/cylinder")) {
760 			v = atoi(tp);
761 			if (v <= 0) {
762 				fprintf(stderr, "line %d: %s: bad %s\n",
763 				    lineno, tp, cp);
764 				errors++;
765 			} else
766 				lp->d_secpercyl = v;
767 			continue;
768 		}
769 		if (streq(cp, "tracks/cylinder")) {
770 			v = atoi(tp);
771 			if (v <= 0) {
772 				fprintf(stderr, "line %d: %s: bad %s\n",
773 				    lineno, tp, cp);
774 				errors++;
775 			} else
776 				lp->d_ntracks = v;
777 			continue;
778 		}
779 		if (streq(cp, "cylinders")) {
780 			v = atoi(tp);
781 			if (v <= 0) {
782 				fprintf(stderr, "line %d: %s: bad %s\n",
783 				    lineno, tp, cp);
784 				errors++;
785 			} else
786 				lp->d_ncylinders = v;
787 			continue;
788 		}
789 		if (streq(cp, "rpm")) {
790 			v = atoi(tp);
791 			if (v <= 0) {
792 				fprintf(stderr, "line %d: %s: bad %s\n",
793 				    lineno, tp, cp);
794 				errors++;
795 			} else
796 				lp->d_rpm = v;
797 			continue;
798 		}
799 		if (streq(cp, "interleave")) {
800 			v = atoi(tp);
801 			if (v <= 0) {
802 				fprintf(stderr, "line %d: %s: bad %s\n",
803 				    lineno, tp, cp);
804 				errors++;
805 			} else
806 				lp->d_interleave = v;
807 			continue;
808 		}
809 		if (streq(cp, "trackskew")) {
810 			v = atoi(tp);
811 			if (v < 0) {
812 				fprintf(stderr, "line %d: %s: bad %s\n",
813 				    lineno, tp, cp);
814 				errors++;
815 			} else
816 				lp->d_trackskew = v;
817 			continue;
818 		}
819 		if (streq(cp, "cylinderskew")) {
820 			v = atoi(tp);
821 			if (v < 0) {
822 				fprintf(stderr, "line %d: %s: bad %s\n",
823 				    lineno, tp, cp);
824 				errors++;
825 			} else
826 				lp->d_cylskew = v;
827 			continue;
828 		}
829 		if (streq(cp, "headswitch")) {
830 			v = atoi(tp);
831 			if (v < 0) {
832 				fprintf(stderr, "line %d: %s: bad %s\n",
833 				    lineno, tp, cp);
834 				errors++;
835 			} else
836 				lp->d_headswitch = v;
837 			continue;
838 		}
839 		if (streq(cp, "track-to-track seek")) {
840 			v = atoi(tp);
841 			if (v < 0) {
842 				fprintf(stderr, "line %d: %s: bad %s\n",
843 				    lineno, tp, cp);
844 				errors++;
845 			} else
846 				lp->d_trkseek = v;
847 			continue;
848 		}
849 		if ('a' <= *cp && *cp <= 'z' && cp[1] == '\0') {
850 			unsigned part = *cp - 'a';
851 
852 			if (part > lp->d_npartitions) {
853 				fprintf(stderr,
854 				    "line %d: bad partition name\n", lineno);
855 				errors++;
856 				continue;
857 			}
858 			pp = &lp->d_partitions[part];
859 #define NXTNUM(n) { \
860 	cp = tp, tp = word(cp); \
861 	if (tp == NULL) \
862 		tp = cp; \
863 	(n) = atoi(cp); \
864      }
865 
866 			NXTNUM(v);
867 			if (v < 0) {
868 				fprintf(stderr,
869 				    "line %d: %s: bad partition size\n",
870 				    lineno, cp);
871 				errors++;
872 			} else
873 				pp->p_size = v;
874 			NXTNUM(v);
875 			if (v < 0) {
876 				fprintf(stderr,
877 				    "line %d: %s: bad partition offset\n",
878 				    lineno, cp);
879 				errors++;
880 			} else
881 				pp->p_offset = v;
882 			cp = tp, tp = word(cp);
883 			cpp = fstypenames;
884 			for (; cpp < &fstypenames[FSMAXTYPES]; cpp++)
885 				if ((s = *cpp) && streq(s, cp)) {
886 					pp->p_fstype = cpp - fstypenames;
887 					goto gottype;
888 				}
889 			if (isdigit(*cp))
890 				v = atoi(cp);
891 			else
892 				v = FSMAXTYPES;
893 			if ((unsigned)v >= FSMAXTYPES) {
894 				fprintf(stderr, "line %d: %s %s\n", lineno,
895 				    "Warning, unknown filesystem type", cp);
896 				v = FS_UNUSED;
897 			}
898 			pp->p_fstype = v;
899 	gottype:
900 
901 			switch (pp->p_fstype) {
902 
903 			case FS_UNUSED:				/* XXX */
904 				NXTNUM(pp->p_fsize);
905 				if (pp->p_fsize == 0)
906 					break;
907 				NXTNUM(v);
908 				pp->p_frag = v / pp->p_fsize;
909 				break;
910 
911 			case FS_BSDFFS:
912 				NXTNUM(pp->p_fsize);
913 				if (pp->p_fsize == 0)
914 					break;
915 				NXTNUM(v);
916 				pp->p_frag = v / pp->p_fsize;
917 				NXTNUM(pp->p_cpg);
918 				break;
919 
920 			default:
921 				break;
922 			}
923 			continue;
924 		}
925 		fprintf(stderr, "line %d: %s: Unknown disklabel field\n",
926 		    lineno, cp);
927 		errors++;
928 	next:
929 		;
930 	}
931 	errors += checklabel(lp);
932 	return (errors == 0);
933 }
934 
935 /*
936  * Check disklabel for errors and fill in
937  * derived fields according to supplied values.
938  */
939 checklabel(lp)
940 	register struct disklabel *lp;
941 {
942 	register struct partition *pp;
943 	int i, errors = 0;
944 	char part;
945 
946 	if (lp->d_secsize == 0) {
947 		fprintf(stderr, "sector size %d\n", lp->d_secsize);
948 		return (1);
949 	}
950 	if (lp->d_nsectors == 0) {
951 		fprintf(stderr, "sectors/track %d\n", lp->d_nsectors);
952 		return (1);
953 	}
954 	if (lp->d_ntracks == 0) {
955 		fprintf(stderr, "tracks/cylinder %d\n", lp->d_ntracks);
956 		return (1);
957 	}
958 	if  (lp->d_ncylinders == 0) {
959 		fprintf(stderr, "cylinders/unit %d\n", lp->d_ncylinders);
960 		errors++;
961 	}
962 	if (lp->d_rpm == 0)
963 		Warning("revolutions/minute %d\n", lp->d_rpm);
964 	if (lp->d_secpercyl == 0)
965 		lp->d_secpercyl = lp->d_nsectors * lp->d_ntracks;
966 	if (lp->d_secperunit == 0)
967 		lp->d_secperunit = lp->d_secpercyl * lp->d_ncylinders;
968 	if (lp->d_bbsize == 0) {
969 		fprintf(stderr, "boot block size %d\n", lp->d_bbsize);
970 		errors++;
971 	} else if (lp->d_bbsize % lp->d_secsize)
972 		Warning("boot block size %% sector-size != 0\n");
973 	if (lp->d_sbsize == 0) {
974 		fprintf(stderr, "super block size %d\n", lp->d_sbsize);
975 		errors++;
976 	} else if (lp->d_sbsize % lp->d_secsize)
977 		Warning("super block size %% sector-size != 0\n");
978 	if (lp->d_npartitions > MAXPARTITIONS)
979 		Warning("number of partitions (%d) > MAXPARTITIONS (%d)\n",
980 		    lp->d_npartitions, MAXPARTITIONS);
981 	for (i = 0; i < lp->d_npartitions; i++) {
982 		part = 'a' + i;
983 		pp = &lp->d_partitions[i];
984 		if (pp->p_size == 0 && pp->p_offset != 0)
985 			Warning("partition %c: size 0, but offset %d\n",
986 			    part, pp->p_offset);
987 #ifdef notdef
988 		if (pp->p_size % lp->d_secpercyl)
989 			Warning("partition %c: size %% cylinder-size != 0\n",
990 			    part);
991 		if (pp->p_offset % lp->d_secpercyl)
992 			Warning("partition %c: offset %% cylinder-size != 0\n",
993 			    part);
994 #endif
995 		if (pp->p_offset > lp->d_secperunit) {
996 			fprintf(stderr,
997 			    "partition %c: offset past end of unit\n", part);
998 			errors++;
999 		}
1000 		if (pp->p_offset + pp->p_size > lp->d_secperunit) {
1001 			fprintf(stderr,
1002 			    "partition %c: partition extends past end of unit\n",
1003 			    part);
1004 			errors++;
1005 		}
1006 	}
1007 	for (; i < MAXPARTITIONS; i++) {
1008 		part = 'a' + i;
1009 		pp = &lp->d_partitions[i];
1010 		if (pp->p_size || pp->p_offset)
1011 			Warning("unused partition %c: size %d offset %d\n",
1012 			    'a' + i, pp->p_size, pp->p_offset);
1013 	}
1014 	return (errors);
1015 }
1016 
1017 /*VARARGS1*/
1018 Warning(fmt, a1, a2, a3, a4, a5)
1019 	char *fmt;
1020 {
1021 
1022 	fprintf(stderr, "Warning, ");
1023 	fprintf(stderr, fmt, a1, a2, a3, a4, a5);
1024 	fprintf(stderr, "\n");
1025 }
1026 
1027 Perror(str)
1028 	char *str;
1029 {
1030 	fputs("disklabel: ", stderr); perror(str);
1031 	exit(4);
1032 }
1033 
1034 usage()
1035 {
1036 #ifdef BOOT
1037 	fprintf(stderr, "%-62s%s\n%-62s%s\n%-62s%s\n%-62s%s\n%-62s%s\n",
1038 "usage: disklabel [-r] disk", "(to read label)",
1039 "or disklabel -w [-r] disk type [ packid ] [ xxboot bootxx ]", "(to write label)",
1040 "or disklabel -e [-r] disk", "(to edit label)",
1041 "or disklabel -R [-r] disk protofile [ type | xxboot bootxx ]", "(to restore label)",
1042 "or disklabel [-NW] disk", "(to write disable/enable label)");
1043 #else
1044 	fprintf(stderr, "%-43s%s\n%-43s%s\n%-43s%s\n%-43s%s\n%-43s%s\n",
1045 "usage: disklabel [-r] disk", "(to read label)",
1046 "or disklabel -w [-r] disk type [ packid ]", "(to write label)",
1047 "or disklabel -e [-r] disk", "(to edit label)",
1048 "or disklabel -R [-r] disk protofile", "(to restore label)",
1049 "or disklabel [-NW] disk", "(to write disable/enable label)");
1050 #endif
1051 	exit(1);
1052 }
1053