xref: /netbsd-src/sbin/disklabel/main.c (revision 5b84b3983f71fd20a534cfa5d1556623a8aaa717)
1 /*	$NetBSD: main.c,v 1.5 2005/08/27 22:21:14 uwe Exp $	*/
2 
3 /*
4  * Copyright (c) 1987, 1993
5  *	The Regents of the University of California.  All rights reserved.
6  *
7  * This code is derived from software contributed to Berkeley by
8  * Symmetric Computer Systems.
9  *
10  * Redistribution and use in source and binary forms, with or without
11  * modification, are permitted provided that the following conditions
12  * are met:
13  * 1. Redistributions of source code must retain the above copyright
14  *    notice, this list of conditions and the following disclaimer.
15  * 2. Redistributions in binary form must reproduce the above copyright
16  *    notice, this list of conditions and the following disclaimer in the
17  *    documentation and/or other materials provided with the distribution.
18  * 3. Neither the name of the University nor the names of its contributors
19  *    may be used to endorse or promote products derived from this software
20  *    without specific prior written permission.
21  *
22  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
23  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
24  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
25  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
26  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
27  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
28  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
29  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
30  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
31  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
32  * SUCH DAMAGE.
33  */
34 
35 #if HAVE_NBTOOL_CONFIG_H
36 #include "nbtool_config.h"
37 #endif
38 
39 #include <sys/cdefs.h>
40 #ifndef lint
41 __COPYRIGHT("@(#) Copyright (c) 1987, 1993\n\
42 	The Regents of the University of California.  All rights reserved.\n");
43 #endif	/* not lint */
44 
45 #ifndef lint
46 #if 0
47 static char sccsid[] = "@(#)disklabel.c	8.4 (Berkeley) 5/4/95";
48 /* from static char sccsid[] = "@(#)disklabel.c	1.2 (Symmetric) 11/28/85"; */
49 #else
50 __RCSID("$NetBSD: main.c,v 1.5 2005/08/27 22:21:14 uwe Exp $");
51 #endif
52 #endif	/* not lint */
53 
54 #include <sys/param.h>
55 #include <sys/file.h>
56 #include <sys/stat.h>
57 #include <sys/wait.h>
58 #define DKTYPENAMES
59 #define FSTYPENAMES
60 
61 #include <ctype.h>
62 #include <err.h>
63 #include <errno.h>
64 #include <unistd.h>
65 #include <signal.h>
66 #include <string.h>
67 #include <stdio.h>
68 #include <stdlib.h>
69 #include <unistd.h>
70 
71 #include <ufs/ufs/dinode.h>
72 #include <ufs/ffs/fs.h>
73 
74 #if HAVE_NBTOOL_CONFIG_H
75 #include <nbinclude/sys/disklabel.h>
76 #include <nbinclude/sys/bootblock.h>
77 #include "../../include/disktab.h"
78 #else
79 #include <sys/ioctl.h>
80 #include <sys/disklabel.h>
81 #include <sys/bootblock.h>
82 #include <util.h>
83 #include <disktab.h>
84 #endif /* HAVE_NBTOOL_CONFIG_H */
85 
86 #include "pathnames.h"
87 #include "extern.h"
88 #include "dkcksum.h"
89 
90 /*
91  * Disklabel: read and write disklabels.
92  * The label is usually placed on one of the first sectors of the disk.
93  * Many machines also place a bootstrap in the same area,
94  * in which case the label is embedded in the bootstrap.
95  * The bootstrap source must leave space at the proper offset
96  * for the label on such machines.
97  */
98 
99 #ifndef BBSIZE
100 #define	BBSIZE	8192			/* size of boot area, with label */
101 #endif
102 
103 #define	DEFEDITOR	_PATH_VI
104 
105 static char	*dkname;
106 static char	tmpfil[MAXPATHLEN];
107 
108 static char	namebuf[BBSIZE], *np = namebuf;
109 static struct	disklabel lab;
110 
111 char	 bootarea[BBSIZE];
112 char	*specname;
113 
114 static enum	{
115 	UNSPEC, EDIT, READ, RESTORE, SETWRITABLE, WRITE, INTERACT
116 } op = UNSPEC;
117 
118 static	int	Fflag;
119 static	int	rflag;
120 static	int	tflag;
121 	int	Cflag;
122 static	int	Iflag;
123 
124 #define COMMON_OPTIONS	"BCFINRWb:ef:irs:tw"
125 
126 #ifdef DEBUG
127 static int	debug;
128 #define OPTIONS	COMMON_OPTIONS "d"
129 #else	/* ! DEBUG */
130 #define OPTIONS	COMMON_OPTIONS
131 #endif	/* ! DEBUG */
132 
133 #ifdef USE_MBR
134 static struct mbr_partition	*dosdp;	/* i386 DOS partition, if found */
135 static struct mbr_partition	*readmbr(int);
136 #endif	/* USE_MBR */
137 
138 #ifdef USE_ACORN
139 static u_int		 filecore_partition_offset;
140 static u_int		 get_filecore_partition(int);
141 static int		 filecore_checksum(u_char *);
142 #endif	/* USE_ACORN */
143 
144 #if defined(USE_MBR) || (defined(USE_ACORN) && defined(notyet))
145 static void		 confirm(const char *);
146 #endif
147 
148 int			 main(int, char *[]);
149 
150 static void		 makedisktab(FILE *, struct disklabel *);
151 static void		 makelabel(const char *, const char *,
152 			    struct disklabel *);
153 static void		 l_perror(const char *);
154 static struct disklabel	*readlabel(int);
155 static struct disklabel	*makebootarea(char *, struct disklabel *, int);
156 static int		 edit(struct disklabel *, int);
157 static int		 editit(void);
158 static char		*skip(char *);
159 static char		*word(char *);
160 static int		 getasciilabel(FILE *, struct disklabel *);
161 static void		 usage(void);
162 static int		 getulong(const char *, char, char **,
163     unsigned long *, unsigned long);
164 #define GETNUM32(a, v)	getulong(a, '\0', NULL, v, UINT32_MAX)
165 #define GETNUM16(a, v)	getulong(a, '\0', NULL, v, UINT16_MAX)
166 #define GETNUM8(a, v)	getulong(a, '\0', NULL, v, UINT8_MAX)
167 
168 #if HAVE_NBTOOL_CONFIG_H
169 #define GETLABELOFFSET()	LABELOFFSET
170 #define GETLABELSECTOR()	LABELSECTOR
171 #else /* HAVE_NBTOOL_CONFIG_H */
172 #define GETLABELOFFSET()	getlabeloffset()
173 #define GETLABELSECTOR()	getlabelsector()
174 #endif
175 
176 int
177 main(int argc, char *argv[])
178 {
179 	struct disklabel *lp;
180 	FILE	*t;
181 	int	 ch, f, writable, error;
182 
183 	error = 0;
184 	while ((ch = getopt(argc, argv, OPTIONS)) != -1)
185 		switch (ch) {
186 		case 'C':
187 			++Cflag;
188 			break;
189 		case 'F':
190 			++Fflag;
191 			break;
192 		case 'I':
193 			++Iflag;
194 			break;
195 		case 'R':
196 			if (op != UNSPEC)
197 				usage();
198 			op = RESTORE;
199 			break;
200 #if !HAVE_NBTOOL_CONFIG_H
201 		case 'N':
202 			if (op != UNSPEC)
203 				usage();
204 			writable = 0;
205 			op = SETWRITABLE;
206 			break;
207 		case 'W':
208 			if (op != UNSPEC)
209 				usage();
210 			writable = 1;
211 			op = SETWRITABLE;
212 			break;
213 #endif /* !HAVE_NBTOOL_CONFIG_H */
214 		case 'e':
215 			if (op != UNSPEC)
216 				usage();
217 			op = EDIT;
218 			break;
219 		case 'f':
220 			if (setdisktab(optarg) == -1)
221 				usage();
222 			break;
223 		case 'i':
224 			if (op != UNSPEC)
225 				usage();
226 			op = INTERACT;
227 			break;
228 		case 't':
229 			++tflag;
230 			break;
231 		case 'r':
232 			++rflag;
233 			break;
234 		case 'w':
235 			if (op != UNSPEC)
236 				usage();
237 			op = WRITE;
238 			break;
239 #ifdef DEBUG
240 		case 'd':
241 			debug++;
242 			break;
243 #endif
244 		case '?':
245 		default:
246 			usage();
247 	}
248 	argc -= optind;
249 	argv += optind;
250 
251 	if (op == UNSPEC)
252 		op = READ;
253 
254 	if (argc < 1)
255 		usage();
256 
257 	if (Iflag && op != EDIT && op != INTERACT)
258 		usage();
259 
260 	dkname = argv[0];
261 #if HAVE_NBTOOL_CONFIG_H
262 	f = open(dkname, op == READ ? O_RDONLY : O_RDWR, 0);
263 	strlcpy(np, dkname, MAXPATHLEN);
264 #else
265 	f = opendisk(dkname, op == READ ? O_RDONLY : O_RDWR, np, MAXPATHLEN, 0);
266 #endif /* HAVE_NBTOOL_CONFIG_H */
267 	specname = np;
268 	np += strlen(specname) + 1;
269 	if (f < 0)
270 		err(4, "%s", specname);
271 
272 #ifdef USE_MBR
273 	/*
274 	 * Check for presence of DOS partition table in
275 	 * master boot record. Return pointer to NetBSD/i386
276 	 * partition, if present.
277 	 */
278 	dosdp = readmbr(f);
279 #endif	/* USE_MBR */
280 
281 #ifdef USE_ACORN
282 	/*
283 	 * Check for the presence of a RiscOS filecore boot block
284 	 * indicating an ADFS file system on the disc.
285 	 * Return the offset to the NetBSD part of the disc if
286 	 * this can be determined.
287 	 * This routine will terminate disklabel if the disc
288 	 * is found to be ADFS only.
289 	 */
290 	filecore_partition_offset = get_filecore_partition(f);
291 #endif	/* USE_ACORN */
292 
293 	switch (op) {
294 
295 	case EDIT:
296 		if (argc != 1)
297 			usage();
298 		lp = readlabel(f);
299 		error = edit(lp, f);
300 		break;
301 
302 	case INTERACT:
303 		if (argc != 1)
304 			usage();
305 		lp = readlabel(f);
306 		/*
307 		 * XXX: Fill some default values so checklabel does not fail
308 		 */
309 		if (lp->d_bbsize == 0)
310 			lp->d_bbsize = BBSIZE;
311 		if (lp->d_sbsize == 0)
312 			lp->d_sbsize = SBLOCKSIZE;
313 		interact(lp, f);
314 		break;
315 
316 	case READ:
317 		if (argc != 1)
318 			usage();
319 		lp = readlabel(f);
320 		if (tflag)
321 			makedisktab(stdout, lp);
322 		else {
323 			showinfo(stdout, lp, specname);
324 			showpartitions(stdout, lp, Cflag);
325 		}
326 		error = checklabel(lp);
327 		if (error)
328 			error += 100;
329 		break;
330 
331 	case RESTORE:
332 		if (argc < 2 || argc > 3)
333 			usage();
334 		lp = makebootarea(bootarea, &lab, f);
335 		if (!(t = fopen(argv[1], "r")))
336 			err(4, "%s", argv[1]);
337 		if (getasciilabel(t, lp))
338 			error = writelabel(f, bootarea, lp);
339 		else
340 			error = 1;
341 		break;
342 
343 #if !HAVE_NBTOOL_CONFIG_H
344 	case SETWRITABLE:
345 		if (ioctl(f, DIOCWLABEL, (char *)&writable) < 0)
346 			err(4, "ioctl DIOCWLABEL");
347 		break;
348 #endif /* !HAVE_NBTOOL_CONFIG_H */
349 
350 	case WRITE:
351 		if (argc < 2 || argc > 3)
352 			usage();
353 		makelabel(argv[1], argc == 3 ? argv[2] : (char *)0, &lab);
354 		lp = makebootarea(bootarea, &lab, f);
355 		*lp = lab;
356 		if (checklabel(lp) == 0)
357 			error = writelabel(f, bootarea, lp);
358 		else
359 			error = 1;
360 		break;
361 
362 	case UNSPEC:
363 		usage();
364 
365 	}
366 	exit(error);
367 }
368 
369 /*
370  * Construct a prototype disklabel from /etc/disktab.  As a side
371  * effect, set the names of the primary and secondary boot files
372  * if specified.
373  */
374 static void
375 makelabel(const char *type, const char *name, struct disklabel *lp)
376 {
377 	struct disklabel *dp;
378 
379 	dp = getdiskbyname(type);
380 	if (dp == NULL)
381 		errx(1, "unknown disk type: %s", type);
382 	*lp = *dp;
383 
384 	/* d_packname is union d_boot[01], so zero */
385 	(void) memset(lp->d_packname, 0, sizeof(lp->d_packname));
386 	if (name)
387 		(void)strncpy(lp->d_packname, name, sizeof(lp->d_packname));
388 }
389 
390 #if defined(USE_MBR) || (defined(USE_ACORN) && defined(notyet))
391 static void
392 confirm(const char *txt)
393 {
394 	int	first, ch;
395 
396 	(void) printf("%s? [n]: ", txt);
397 	(void) fflush(stdout);
398 	first = ch = getchar();
399 	while (ch != '\n' && ch != EOF)
400 		ch = getchar();
401 	if (first != 'y' && first != 'Y')
402 		exit(0);
403 }
404 #endif	/* USE_MBR || USE_ACORN && notyet */
405 
406 int
407 writelabel(int f, char *boot, struct disklabel *lp)
408 {
409 	int	writable;
410 	off_t	sectoffset;
411 
412 	sectoffset = 0;
413 	lp->d_magic = DISKMAGIC;
414 	lp->d_magic2 = DISKMAGIC;
415 	lp->d_checksum = 0;
416 	lp->d_checksum = dkcksum(lp);
417 
418 	if (Fflag || rflag || Iflag)
419 	{
420 #ifdef USE_MBR
421 		struct partition *pp = &lp->d_partitions[2];
422 
423 		/*
424 		 * If NetBSD/i386 DOS partition is missing, or if
425 		 * the label to be written is not within partition,
426 		 * prompt first. Need to allow this in case operator
427 		 * wants to convert the drive for dedicated use.
428 		 */
429 		if (dosdp) {
430 			if (dosdp->mbrp_start != pp->p_offset) {
431 				printf("NetBSD slice at %u, "
432 				    "partition C at %u\n", dosdp->mbrp_start,
433 				    pp->p_offset);
434 				confirm("Write outside MBR partition");
435 			}
436 		        sectoffset = (off_t)pp->p_offset * lp->d_secsize;
437 		} else {
438 			sectoffset = 0;
439 		}
440 #endif	/* USE_MBR */
441 
442 #ifdef USE_ACORN
443 		/* XXX */
444 		sectoffset = (off_t)filecore_partition_offset * DEV_BSIZE;
445 #endif	/* USE_ACORN */
446 
447 		/*
448 		 * First set the kernel disk label,
449 		 * then write a label to the raw disk.
450 		 * If the SDINFO ioctl fails because it is unimplemented,
451 		 * keep going; otherwise, the kernel consistency checks
452 		 * may prevent us from changing the current (in-core)
453 		 * label.
454 		 */
455 #if !HAVE_NBTOOL_CONFIG_H
456 		if (!Fflag && ioctl(f, DIOCSDINFO, lp) < 0 &&
457 		    errno != ENODEV && errno != ENOTTY) {
458 			l_perror("ioctl DIOCSDINFO");
459 			return (1);
460 		}
461 #endif /* HAVE_NBTOOL_CONFIG_H */
462 		if (lseek(f, sectoffset, SEEK_SET) < 0) {
463 			perror("lseek");
464 			return (1);
465 		}
466 		/*
467 		 * write enable label sector before write (if necessary),
468 		 * disable after writing.
469 		 */
470 		writable = 1;
471 #if !HAVE_NBTOOL_CONFIG_H
472 		if (!Fflag && ioctl(f, DIOCWLABEL, &writable) < 0)
473 			perror("ioctl DIOCWLABEL");
474 #endif /* HAVE_NBTOOL_CONFIG_H */
475 
476 #ifdef __alpha__
477 		/*
478 		 * The Alpha requires that the boot block be checksummed.
479 		 * The NetBSD/alpha disklabel.h provides a macro to do it.
480 		 */
481 		{
482 			struct alpha_boot_block *bb;
483 
484 			bb = (struct alpha_boot_block *)boot;
485 			ALPHA_BOOT_BLOCK_CKSUM(bb, &bb->bb_cksum);
486 		}
487 #endif	/* __alpha__ */
488 
489 		if (write(f, boot, lp->d_bbsize) != lp->d_bbsize) {
490 			perror("write");
491 			return (1);
492 		}
493 
494 		writable = 0;
495 #if !HAVE_NBTOOL_CONFIG_H
496 		if (!Fflag && ioctl(f, DIOCWLABEL, &writable) < 0)
497 			perror("ioctl DIOCWLABEL");
498 		/*
499 		 * Now issue a DIOCWDINFO. This will let the kernel convert the
500 		 * disklabel to some machdep format if needed.
501 		 */
502 		if (!Fflag && ioctl(f, DIOCWDINFO, lp) < 0) {
503 			l_perror("ioctl DIOCWDINFO");
504 			return (1);
505 		}
506 #endif /* !HAVE_NBTOOL_CONFIG_H */
507 	} else {
508 #if !HAVE_NBTOOL_CONFIG_H
509 		if (ioctl(f, DIOCWDINFO, lp) < 0) {
510 			l_perror("ioctl DIOCWDINFO");
511 			return (1);
512 		}
513 #else
514 		errx(1, "use -F, -r, or -I");
515 #endif /* HAVE_NBTOOL_CONFIG_H */
516 	}
517 
518 #ifdef __vax__
519 	if (lp->d_type == DTYPE_SMD && lp->d_flags & D_BADSECT) {
520 		daddr_t	alt;
521 		int	i;
522 
523 		alt = lp->d_ncylinders * lp->d_secpercyl - lp->d_nsectors;
524 		for (i = 1; i < 11 && i < lp->d_nsectors; i += 2) {
525 			(void)lseek(f, (off_t)(alt + i) * lp->d_secsize,
526 			    SEEK_SET);
527 			if (write(f, boot, lp->d_secsize) < lp->d_secsize)
528 				warn("alternate label %d write", i/2);
529 		}
530 	}
531 #endif	/* __vax__ */
532 
533 	return (0);
534 }
535 
536 static void
537 l_perror(const char *s)
538 {
539 
540 	switch (errno) {
541 
542 	case ESRCH:
543 		warnx("%s: No disk label on disk;\n"
544 		    "use \"disklabel -I\" to install initial label", s);
545 		break;
546 
547 	case EINVAL:
548 		warnx("%s: Label magic number or checksum is wrong!\n"
549 		    "(disklabel or kernel is out of date?)", s);
550 		break;
551 
552 	case EBUSY:
553 		warnx("%s: Open partition would move or shrink", s);
554 		break;
555 
556 	case EXDEV:
557 		warnx("%s: Labeled partition or 'a' partition must start"
558 		      " at beginning of disk", s);
559 		break;
560 
561 	default:
562 		warn("%s", s);
563 		break;
564 	}
565 }
566 
567 #ifdef USE_MBR
568 /*
569  * Fetch DOS partition table from disk.
570  */
571 static struct mbr_partition *
572 readmbr(int f)
573 {
574 	struct mbr_partition *dp;
575 	struct mbr_sector mbr;
576 	int part;
577 	u_int ext_base, next_ext, this_ext;
578 	static struct mbr_partition netbsd_part;
579 
580 	/*
581 	 * Don't (yet) know disk geometry (BIOS), use
582 	 * partition table to find NetBSD/i386 partition, and obtain
583 	 * disklabel from there.
584 	 */
585 
586 	ext_base = 0;
587 	next_ext = 0;
588 	for (;;) {
589 		this_ext = next_ext;
590 		next_ext = 0;
591 		if (pread(f, &mbr, sizeof mbr, this_ext * (off_t)DEV_BSIZE)
592 		    != sizeof(mbr)) {
593 			warn("Can't read master boot record %d", this_ext);
594 			return 0;
595 		}
596 
597 		/* Check if table is valid. */
598 		if (mbr.mbr_magic != htole16(MBR_MAGIC)) {
599 			warnx("Invalid signature in mbr record %d", this_ext);
600 			return 0;
601 		}
602 
603 		dp = &mbr.mbr_parts[0];
604 #if defined(_no_longer_needed) && !defined(__i386__) && !defined(__x86_64__)
605 		/* avoid alignment error */
606 		memcpy(mbr, dp, MBR_PART_COUNT * sizeof(*dp));
607 		dp = (struct mbr_partition *)mbr;
608 #endif	/* ! __i386__ */
609 
610 		/* Find NetBSD partition. */
611 		for (part = 0; part < MBR_PART_COUNT; dp++, part++) {
612 			dp->mbrp_start = le32toh(dp->mbrp_start);
613 			dp->mbrp_size = le32toh(dp->mbrp_size);
614 			switch (dp->mbrp_type) {
615 			case MBR_PTYPE_NETBSD:
616 				netbsd_part = *dp;
617 				break;
618 			case MBR_PTYPE_EXT:
619 			case MBR_PTYPE_EXT_LBA:
620 			case MBR_PTYPE_EXT_LNX:
621 				next_ext = dp->mbrp_start;
622 				continue;
623 #ifdef COMPAT_386BSD_MBRPART
624 			case MBR_PTYPE_386BSD:
625 				if (ext_base == 0)
626 					netbsd_part = *dp;
627 				continue;
628 #endif	/* COMPAT_386BSD_MBRPART */
629 			default:
630 				continue;
631 			}
632 			break;
633 		}
634 		if (part < MBR_PART_COUNT)
635 			/* We found a netbsd partition */
636 			break;
637 		if (next_ext == 0)
638 			/* No more extended partitions */
639 			break;
640 		next_ext += ext_base;
641 		if (ext_base == 0)
642 			ext_base = next_ext;
643 
644 		if (next_ext <= this_ext) {
645 			warnx("Invalid extended chain %x <= %x",
646 				next_ext, this_ext);
647 			break;
648 		}
649 	}
650 
651 	if (netbsd_part.mbrp_type == 0)
652 		return 0;
653 
654 	netbsd_part.mbrp_start += this_ext;
655 	return &netbsd_part;
656 }
657 #endif	/* USE_MBR */
658 
659 #ifdef USE_ACORN
660 /*
661  * static int filecore_checksum(u_char *bootblock)
662  *
663  * Calculates the filecore boot block checksum. This is used to validate
664  * a filecore boot block on the disk.  If a boot block is validated then
665  * it is used to locate the partition table. If the boot block is not
666  * validated, it is assumed that the whole disk is NetBSD.
667  *
668  * The basic algorithm is:
669  *
670  *	for (each byte in block, excluding checksum) {
671  *		sum += byte;
672  *		if (sum > 255)
673  *			sum -= 255;
674  *	}
675  *
676  * That's equivalent to summing all of the bytes in the block
677  * (excluding the checksum byte, of course), then calculating the
678  * checksum as "cksum = sum - ((sum - 1) / 255) * 255)".  That
679  * expression may or may not yield a faster checksum function,
680  * but it's easier to reason about.
681  *
682  * Note that if you have a block filled with bytes of a single
683  * value "X" (regardless of that value!) and calculate the cksum
684  * of the block (excluding the checksum byte), you will _always_
685  * end up with a checksum of X.  (Do the math; that can be derived
686  * from the checksum calculation function!)  That means that
687  * blocks which contain bytes which all have the same value will
688  * always checksum properly.  That's a _very_ unlikely occurence
689  * (probably impossible, actually) for a valid filecore boot block,
690  * so we treat such blocks as invalid.
691  */
692 static int
693 filecore_checksum(u_char *bootblock)
694 {
695 	u_char	byte0, accum_diff;
696 	u_int	sum;
697 	int	i;
698 
699 	sum = 0;
700 	accum_diff = 0;
701 	byte0 = bootblock[0];
702 
703 	/*
704 	 * Sum the contents of the block, keeping track of whether
705 	 * or not all bytes are the same.  If 'accum_diff' ends up
706 	 * being zero, all of the bytes are, in fact, the same.
707 	 */
708 	for (i = 0; i < 511; ++i) {
709 		sum += bootblock[i];
710 		accum_diff |= bootblock[i] ^ byte0;
711 	}
712 
713 	/*
714 	 * Check to see if the checksum byte is the same as the
715 	 * rest of the bytes, too.  (Note that if all of the bytes
716 	 * are the same except the checksum, a checksum compare
717 	 * won't succeed, but that's not our problem.)
718 	 */
719 	accum_diff |= bootblock[i] ^ byte0;
720 
721 	/* All bytes in block are the same; call it invalid. */
722 	if (accum_diff == 0)
723 		return (-1);
724 
725 	return (sum - ((sum - 1) / 255) * 255);
726 }
727 
728 /*
729  * Fetch filecore bootblock from disk and analyse it
730  */
731 static u_int
732 get_filecore_partition(int f)
733 {
734 	struct filecore_bootblock	*fcbb;
735 	static char	bb[DEV_BSIZE];
736 	u_int		offset;
737 
738 	if (lseek(f, (off_t)FILECORE_BOOT_SECTOR * DEV_BSIZE, SEEK_SET) < 0 ||
739 	    read(f, bb, sizeof(bb)) != sizeof(bb))
740 		err(4, "can't read filecore boot block");
741 	fcbb = (struct filecore_bootblock *)bb;
742 
743 	/* Check if table is valid. */
744 	if (filecore_checksum(bb) != fcbb->checksum)
745 		return (0);
746 
747 	/*
748 	 * Check for NetBSD/arm32 (RiscBSD) partition marker.
749 	 * If found the NetBSD disklabel location is easy.
750 	 */
751 	offset = (fcbb->partition_cyl_low + (fcbb->partition_cyl_high << 8))
752 	    * fcbb->heads * fcbb->secspertrack;
753 	if (fcbb->partition_type == PARTITION_FORMAT_RISCBSD)
754 		return (offset);
755 	else if (fcbb->partition_type == PARTITION_FORMAT_RISCIX) {
756 		struct riscix_partition_table	*riscix_part;
757 		int				 loop;
758 
759 		/*
760 		 * Read the RISCiX partition table and search for the
761 		 * first partition named "RiscBSD", "NetBSD", or "Empty:"
762 		 *
763 		 * XXX is use of 'Empty:' really desirable?! -- cgd
764 		 */
765 
766 		if (lseek(f, (off_t)offset * DEV_BSIZE, SEEK_SET) < 0 ||
767 		    read(f, bb, sizeof(bb)) != sizeof(bb))
768 			err(4, "can't read riscix partition table");
769 		riscix_part = (struct riscix_partition_table *)bb;
770 
771 		for (loop = 0; loop < NRISCIX_PARTITIONS; ++loop) {
772 			if (strcmp(riscix_part->partitions[loop].rp_name,
773 			    "RiscBSD") == 0 ||
774 			    strcmp(riscix_part->partitions[loop].rp_name,
775 			    "NetBSD") == 0 ||
776 			    strcmp(riscix_part->partitions[loop].rp_name,
777 			    "Empty:") == 0) {
778 				offset = riscix_part->partitions[loop].rp_start;
779 				break;
780 			}
781 		}
782 		if (loop == NRISCIX_PARTITIONS) {
783 			/*
784 			 * Valid filecore boot block, RISCiX partition table
785 			 * but no NetBSD partition. We should leave this
786 			 * disc alone.
787 			 */
788 			errx(4, "cannot label: no NetBSD partition found"
789 				" in RISCiX partition table");
790 		}
791 		return (offset);
792 	} else {
793 		/*
794 		 * Valid filecore boot block and no non-ADFS partition.
795 		 * This means that the whole disc is allocated for ADFS
796 		 * so do not trash ! If the user really wants to put a
797 		 * NetBSD disklabel on the disc then they should remove
798 		 * the filecore boot block first with dd.
799 		 */
800 		errx(4, "cannot label: filecore-only disk"
801 			" (no non-ADFS partition)");
802 	}
803 	return (0);
804 }
805 #endif	/* USE_ACORN */
806 
807 /*
808  * Fetch disklabel for disk.
809  * Use ioctl to get label unless -r flag is given.
810  */
811 static struct disklabel *
812 readlabel(int f)
813 {
814 	struct disklabel *lp;
815 
816 	if (Fflag || rflag || Iflag) {
817 		const char *msg;
818 		off_t	 sectoffset;
819 
820 		msg = NULL;
821 		sectoffset = 0;
822 
823 #ifdef USE_MBR
824 		if (dosdp)
825 			sectoffset = (off_t)dosdp->mbrp_start * DEV_BSIZE;
826 #endif	/* USE_MBR */
827 
828 #ifdef USE_ACORN
829 		/* XXX */
830 		sectoffset = (off_t)filecore_partition_offset * DEV_BSIZE;
831 #endif	/* USE_ACORN */
832 
833 		if (lseek(f, sectoffset, SEEK_SET) < 0 ||
834 		    read(f, bootarea, BBSIZE) != BBSIZE)
835 			err(4, "%s", specname);
836 
837 		msg = "no disklabel";
838 		for (lp = (struct disklabel *)bootarea;
839 		    lp <= (struct disklabel *)(bootarea + BBSIZE - sizeof(*lp));
840 		    lp = (struct disklabel *)((char *)lp + sizeof(long))) {
841 			if (lp->d_magic == DISKMAGIC &&
842 			    lp->d_magic2 == DISKMAGIC) {
843 				if (lp->d_npartitions <= MAXPARTITIONS &&
844 				    dkcksum(lp) == 0)
845 					return (lp);
846 				msg = "disk label corrupted";
847 			}
848 		}
849 		if (msg != NULL && !Iflag)
850 			errx(1, "%s", msg);
851 #if HAVE_NBTOOL_CONFIG_H
852 		goto err;
853 #else
854 		/*
855 		 * There was no label on the disk. Get the fictious one
856 		 * as a basis for initialisation.
857 		 */
858 		lp = makebootarea(bootarea, &lab, f);
859 		if (ioctl(f, DIOCGDINFO, lp) < 0 &&
860 		    ioctl(f, DIOCGDEFLABEL, lp) < 0)
861 			goto err;
862 #endif /* HAVE_NBTOOL_CONFIG_H */
863 	} else {
864 #ifdef HAVE_NBTOOL_CONFIG_H
865 		goto err;
866 #else
867 		lp = &lab;
868 		if (ioctl(f, DIOCGDINFO, lp) < 0)
869 			err(4, "ioctl DIOCGDINFO");
870 #endif /* HAVE_NBTOOL_CONFIG_H */
871 	}
872 	return (lp);
873 err:
874 	errx(1, "could not get initial label");
875 }
876 
877 /*
878  * Construct a bootarea (d_bbsize bytes) in the specified buffer ``boot''
879  * Returns a pointer to the disklabel portion of the bootarea.
880  */
881 static struct disklabel *
882 makebootarea(char *boot, struct disklabel *dp, int f)
883 {
884 	struct disklabel *lp;
885 	char		*p;
886 	daddr_t		 lsec;
887 	off_t		 loff;
888 
889 	if ((lsec = GETLABELSECTOR()) < 0)
890 		err(4, "getlabelsector()");
891 	if ((loff = GETLABELOFFSET()) < 0)
892 		err(4, "getlabeloffset()");
893 
894 	/* XXX */
895 	if (dp->d_secsize == 0) {
896 		dp->d_secsize = DEV_BSIZE;
897 		dp->d_bbsize = BBSIZE;
898 	}
899 	lp = (struct disklabel *) (boot + (lsec * dp->d_secsize) + loff);
900 	(void) memset(lp, 0, sizeof(*lp));
901 
902 #ifdef SAVEBOOTAREA
903 	/*
904 	 * We must read the current bootarea so we don't clobber the
905 	 * existing boot block, if any.
906 	 */
907 	if (rflag || Iflag) {
908 		off_t	sectoffset;
909 
910 		sectoffset = 0;
911 		if (lseek(f, sectoffset, SEEK_SET) < 0 ||
912 		    read(f, boot, BBSIZE) != BBSIZE)
913 			err(4, "%s", specname);
914 		(void) memset(lp, 0, sizeof(*lp));
915 	}
916 #endif	/* SAVEBOOTAREA */
917 
918 	/*
919 	 * Make sure no part of the bootstrap is written in the area
920 	 * reserved for the label.
921 	 */
922 	for (p = (char *)lp; p < (char *)lp + sizeof(struct disklabel); p++)
923 		if (*p)
924 			errx(2, "Bootstrap doesn't leave room for disk label");
925 	return (lp);
926 }
927 
928 static void
929 makedisktab(FILE *f, struct disklabel *lp)
930 {
931 	int	 i;
932 	const char *did;
933 	struct partition *pp;
934 
935 	did = "\\\n\t:";
936 	(void) fprintf(f, "%.*s|Automatically generated label:\\\n\t:dt=",
937 	    (int) sizeof(lp->d_typename), lp->d_typename);
938 	if ((unsigned) lp->d_type < DKMAXTYPES)
939 		(void) fprintf(f, "%s:", dktypenames[lp->d_type]);
940 	else
941 		(void) fprintf(f, "unknown%d:", lp->d_type);
942 
943 	(void) fprintf(f, "se#%d:", lp->d_secsize);
944 	(void) fprintf(f, "ns#%d:", lp->d_nsectors);
945 	(void) fprintf(f, "nt#%d:", lp->d_ntracks);
946 	(void) fprintf(f, "sc#%d:", lp->d_secpercyl);
947 	(void) fprintf(f, "nc#%d:", lp->d_ncylinders);
948 
949 	if ((lp->d_secpercyl * lp->d_ncylinders) != lp->d_secperunit) {
950 		(void) fprintf(f, "%ssu#%d:", did, lp->d_secperunit);
951 		did = "";
952 	}
953 	if (lp->d_rpm != 3600) {
954 		(void) fprintf(f, "%srm#%d:", did, lp->d_rpm);
955 		did = "";
956 	}
957 	if (lp->d_interleave != 1) {
958 		(void) fprintf(f, "%sil#%d:", did, lp->d_interleave);
959 		did = "";
960 	}
961 	if (lp->d_trackskew != 0) {
962 		(void) fprintf(f, "%ssk#%d:", did, lp->d_trackskew);
963 		did = "";
964 	}
965 	if (lp->d_cylskew != 0) {
966 		(void) fprintf(f, "%scs#%d:", did, lp->d_cylskew);
967 		did = "";
968 	}
969 	if (lp->d_headswitch != 0) {
970 		(void) fprintf(f, "%shs#%d:", did, lp->d_headswitch);
971 		did = "";
972 	}
973 	if (lp->d_trkseek != 0) {
974 		(void) fprintf(f, "%sts#%d:", did, lp->d_trkseek);
975 		did = "";
976 	}
977 #ifdef notyet
978 	(void) fprintf(f, "drivedata: ");
979 	for (i = NDDATA - 1; i >= 0; i--)
980 		if (lp->d_drivedata[i])
981 			break;
982 	if (i < 0)
983 		i = 0;
984 	for (j = 0; j <= i; j++)
985 		(void) fprintf(f, "%d ", lp->d_drivedata[j]);
986 #endif	/* notyet */
987 	pp = lp->d_partitions;
988 	for (i = 0; i < lp->d_npartitions; i++, pp++) {
989 		if (pp->p_size) {
990 			char c = 'a' + i;
991 			(void) fprintf(f, "\\\n\t:");
992 			(void) fprintf(f, "p%c#%d:", c, pp->p_size);
993 			(void) fprintf(f, "o%c#%d:", c, pp->p_offset);
994 			if (pp->p_fstype != FS_UNUSED) {
995 				if ((unsigned) pp->p_fstype < FSMAXTYPES)
996 					(void) fprintf(f, "t%c=%s:", c,
997 					    fstypenames[pp->p_fstype]);
998 				else
999 					(void) fprintf(f, "t%c=unknown%d:",
1000 					    c, pp->p_fstype);
1001 			}
1002 			switch (pp->p_fstype) {
1003 
1004 			case FS_UNUSED:
1005 				break;
1006 
1007 			case FS_BSDFFS:
1008 			case FS_BSDLFS:
1009 			case FS_EX2FS:
1010 			case FS_ADOS:
1011 			case FS_APPLEUFS:
1012 				(void) fprintf(f, "b%c#%d:", c,
1013 				    pp->p_fsize * pp->p_frag);
1014 				(void) fprintf(f, "f%c#%d:", c, pp->p_fsize);
1015 				break;
1016 			default:
1017 				break;
1018 			}
1019 		}
1020 	}
1021 	(void) fprintf(f, "\n");
1022 	(void) fflush(f);
1023 }
1024 
1025 static int
1026 edit(struct disklabel *lp, int f)
1027 {
1028 	struct disklabel label;
1029 	const char *tmpdir;
1030 	int	 first, ch, fd;
1031 	FILE	*fp;
1032 
1033 	if ((tmpdir = getenv("TMPDIR")) == NULL)
1034 		tmpdir = _PATH_TMP;
1035 	(void)snprintf(tmpfil, sizeof(tmpfil), "%s/%s", tmpdir, TMPFILE);
1036 	if ((fd = mkstemp(tmpfil)) == -1 || (fp = fdopen(fd, "w")) == NULL) {
1037 		warn("%s", tmpfil);
1038 		return (1);
1039 	}
1040 	(void)fchmod(fd, 0600);
1041 	showinfo(fp, lp, specname);
1042 	showpartitions(fp, lp, Cflag);
1043 	(void) fclose(fp);
1044 	for (;;) {
1045 		if (!editit())
1046 			break;
1047 		fp = fopen(tmpfil, "r");
1048 		if (fp == NULL) {
1049 			warn("%s", tmpfil);
1050 			break;
1051 		}
1052 		(void) memset(&label, 0, sizeof(label));
1053 		if (getasciilabel(fp, &label)) {
1054 			*lp = label;
1055 			if (writelabel(f, bootarea, lp) == 0) {
1056 				(void) unlink(tmpfil);
1057 				return (0);
1058 			}
1059 		}
1060 		(void) printf("re-edit the label? [y]: ");
1061 		(void) fflush(stdout);
1062 		first = ch = getchar();
1063 		while (ch != '\n' && ch != EOF)
1064 			ch = getchar();
1065 		if (first == 'n' || first == 'N')
1066 			break;
1067 	}
1068 	(void)unlink(tmpfil);
1069 	return (1);
1070 }
1071 
1072 static int
1073 editit(void)
1074 {
1075 	int pid, xpid;
1076 	int status;
1077 	sigset_t nsigset, osigset;
1078 
1079 	sigemptyset(&nsigset);
1080 	sigaddset(&nsigset, SIGINT);
1081 	sigaddset(&nsigset, SIGQUIT);
1082 	sigaddset(&nsigset, SIGHUP);
1083 	sigprocmask(SIG_BLOCK, &nsigset, &osigset);
1084 	while ((pid = fork()) < 0) {
1085 		if (errno != EAGAIN) {
1086 			sigprocmask(SIG_SETMASK, &osigset, (sigset_t *)0);
1087 			warn("fork");
1088 			return (0);
1089 		}
1090 		sleep(1);
1091 	}
1092 	if (pid == 0) {
1093 		const char *ed;
1094 		char *buf;
1095 		int retval;
1096 
1097 		sigprocmask(SIG_SETMASK, &osigset, (sigset_t *)0);
1098 		setgid(getgid());
1099 		setuid(getuid());
1100 		if ((ed = getenv("EDITOR")) == (char *)0)
1101 			ed = DEFEDITOR;
1102 		/*
1103 		 * Jump through a few extra hoops in case someone's editor
1104 		 * is "editor arg1 arg2".
1105 		 */
1106 		asprintf(&buf, "%s %s", ed, tmpfil);
1107 		if (!buf)
1108 			err(1, "malloc");
1109 		retval = execlp(_PATH_BSHELL, _PATH_BSHELL, "-c", buf, NULL);
1110 		if (retval == -1)
1111 			perror(ed);
1112 		exit(retval);
1113 	}
1114 	while ((xpid = wait(&status)) >= 0)
1115 		if (xpid == pid)
1116 			break;
1117 	sigprocmask(SIG_SETMASK, &osigset, (sigset_t *)0);
1118 	return (!status);
1119 }
1120 
1121 static char *
1122 skip(char *cp)
1123 {
1124 
1125 	cp += strspn(cp, " \t");
1126 	if (*cp == '\0')
1127 		return (NULL);
1128 	return (cp);
1129 }
1130 
1131 static char *
1132 word(char *cp)
1133 {
1134 
1135 	if (cp == NULL || *cp == '\0')
1136 		return (NULL);
1137 
1138 	cp += strcspn(cp, " \t");
1139 	if (*cp == '\0')
1140 		return (NULL);
1141 	*cp++ = '\0';
1142 	cp += strspn(cp, " \t");
1143 	if (*cp == '\0')
1144 		return (NULL);
1145 	return (cp);
1146 }
1147 
1148 #define _CHECKLINE \
1149 	if (tp == NULL || *tp == '\0') {			\
1150 		warnx("line %d: too few fields", lineno);	\
1151 		errors++;					\
1152 		break;						\
1153 	}
1154 
1155 #define __CHECKLINE \
1156 	if (*tp == NULL || **tp == '\0') {			\
1157 		warnx("line %d: too few fields", lineno);	\
1158 		*tp = _error_;					\
1159 		return 0;					\
1160 	}
1161 
1162 static char _error_[] = "";
1163 #define NXTNUM(n)	if ((n = nxtnum(&tp, lineno),0) + tp != _error_) \
1164 			; else goto error
1165 #define NXTXNUM(n)	if ((n = nxtxnum(&tp, lp, lineno),0) + tp != _error_) \
1166 			; else goto error
1167 
1168 static unsigned long
1169 nxtnum(char **tp, int lineno)
1170 {
1171 	char *cp;
1172 	unsigned long v;
1173 
1174 	__CHECKLINE
1175 	if (getulong(*tp, '\0', &cp, &v, UINT32_MAX) != 0) {
1176 		warnx("line %d: syntax error", lineno);
1177 		*tp = _error_;
1178 		return 0;
1179 	}
1180 	*tp = cp;
1181 	return v;
1182 }
1183 
1184 static unsigned long
1185 nxtxnum(char **tp, struct disklabel *lp, int lineno)
1186 {
1187 	char	*cp, *ncp;
1188 	unsigned long n, v;
1189 
1190 	__CHECKLINE
1191 	cp = *tp;
1192 	if (getulong(cp, '/', &ncp, &n, UINT32_MAX) != 0)
1193 		goto bad;
1194 
1195 	if (*ncp == '/') {
1196 		n *= lp->d_secpercyl;
1197 		cp = ncp + 1;
1198 		if (getulong(cp, '/', &ncp, &v, UINT32_MAX) != 0)
1199 			goto bad;
1200 		n += v * lp->d_nsectors;
1201 		cp = ncp + 1;
1202 		if (getulong(cp, '\0', &ncp, &v, UINT32_MAX) != 0)
1203 			goto bad;
1204 		n += v;
1205 	}
1206 	*tp = ncp;
1207 	return n;
1208 bad:
1209 	warnx("line %d: invalid format", lineno);
1210 	*tp = _error_;
1211 	return 0;
1212 }
1213 
1214 /*
1215  * Read an ascii label in from fd f,
1216  * in the same format as that put out by showinfo() and showpartitions(),
1217  * and fill in lp.
1218  */
1219 static int
1220 getasciilabel(FILE *f, struct disklabel *lp)
1221 {
1222 	const char *const *cpp, *s;
1223 	struct partition *pp;
1224 	char	*cp, *tp, line[BUFSIZ], tbuf[15];
1225 	int	 lineno, errors;
1226 	unsigned long v;
1227 	unsigned int part;
1228 
1229 	lineno = 0;
1230 	errors = 0;
1231 	lp->d_bbsize = BBSIZE;				/* XXX */
1232 	lp->d_sbsize = SBLOCKSIZE;			/* XXX */
1233 	while (fgets(line, sizeof(line) - 1, f)) {
1234 		lineno++;
1235 		if ((cp = strpbrk(line, "#\r\n")) != NULL)
1236 			*cp = '\0';
1237 		cp = skip(line);
1238 		if (cp == NULL)     /* blank line or comment line */
1239 			continue;
1240 		tp = strchr(cp, ':'); /* everything has a colon in it */
1241 		if (tp == NULL) {
1242 			warnx("line %d: syntax error", lineno);
1243 			errors++;
1244 			continue;
1245 		}
1246 		*tp++ = '\0', tp = skip(tp);
1247 		if (!strcmp(cp, "type")) {
1248 			if (tp == NULL) {
1249 				strlcpy(tbuf, "unknown", sizeof(tbuf));
1250 				tp = tbuf;
1251 			}
1252 			cpp = dktypenames;
1253 			for (; cpp < &dktypenames[DKMAXTYPES]; cpp++)
1254 				if ((s = *cpp) && !strcasecmp(s, tp)) {
1255 					lp->d_type = cpp - dktypenames;
1256 					goto next;
1257 				}
1258 			if (GETNUM16(tp, &v) != 0) {
1259 				warnx("line %d: syntax error", lineno);
1260 				errors++;
1261 				continue;
1262 			}
1263 			if (v >= DKMAXTYPES)
1264 				warnx("line %d: warning, unknown disk type: %s",
1265 				    lineno, tp);
1266 			lp->d_type = v;
1267 			continue;
1268 		}
1269 		if (!strcmp(cp, "flags")) {
1270 			for (v = 0; (cp = tp) && *cp != '\0';) {
1271 				tp = word(cp);
1272 				if (!strcasecmp(cp, "removable"))
1273 					v |= D_REMOVABLE;
1274 				else if (!strcasecmp(cp, "ecc"))
1275 					v |= D_ECC;
1276 				else if (!strcasecmp(cp, "badsect"))
1277 					v |= D_BADSECT;
1278 				else {
1279 					warnx("line %d: bad flag: %s",
1280 					    lineno, cp);
1281 					errors++;
1282 				}
1283 			}
1284 			lp->d_flags = v;
1285 			continue;
1286 		}
1287 		if (!strcmp(cp, "drivedata")) {
1288 			int i;
1289 
1290 			for (i = 0; (cp = tp) && *cp != '\0' && i < NDDATA;) {
1291 				if (GETNUM32(cp, &v) != 0) {
1292 					warnx("line %d: bad drive data",
1293 					    lineno);
1294 					errors++;
1295 				} else
1296 					lp->d_drivedata[i] = v;
1297 				i++;
1298 				tp = word(cp);
1299 			}
1300 			continue;
1301 		}
1302 		if (sscanf(cp, "%lu partitions", &v) == 1) {
1303 			if (v == 0 || v > MAXPARTITIONS) {
1304 				warnx("line %d: bad # of partitions", lineno);
1305 				lp->d_npartitions = MAXPARTITIONS;
1306 				errors++;
1307 			} else
1308 				lp->d_npartitions = v;
1309 			continue;
1310 		}
1311 		if (tp == NULL) {
1312 			tbuf[0] = '\0';
1313 			tp = tbuf;
1314 		}
1315 		if (!strcmp(cp, "disk")) {
1316 			strncpy(lp->d_typename, tp, sizeof(lp->d_typename));
1317 			continue;
1318 		}
1319 		if (!strcmp(cp, "label")) {
1320 			strncpy(lp->d_packname, tp, sizeof(lp->d_packname));
1321 			continue;
1322 		}
1323 		if (!strcmp(cp, "bytes/sector")) {
1324 			if (GETNUM32(tp, &v) != 0 || v <= 0 || (v % 512) != 0) {
1325 				warnx("line %d: bad %s: %s", lineno, cp, tp);
1326 				errors++;
1327 			} else
1328 				lp->d_secsize = v;
1329 			continue;
1330 		}
1331 		if (!strcmp(cp, "sectors/track")) {
1332 			if (GETNUM32(tp, &v) != 0) {
1333 				warnx("line %d: bad %s: %s", lineno, cp, tp);
1334 				errors++;
1335 			} else
1336 				lp->d_nsectors = v;
1337 			continue;
1338 		}
1339 		if (!strcmp(cp, "sectors/cylinder")) {
1340 			if (GETNUM32(tp, &v) != 0) {
1341 				warnx("line %d: bad %s: %s", lineno, cp, tp);
1342 				errors++;
1343 			} else
1344 				lp->d_secpercyl = v;
1345 			continue;
1346 		}
1347 		if (!strcmp(cp, "tracks/cylinder")) {
1348 			if (GETNUM32(tp, &v) != 0) {
1349 				warnx("line %d: bad %s: %s", lineno, cp, tp);
1350 				errors++;
1351 			} else
1352 				lp->d_ntracks = v;
1353 			continue;
1354 		}
1355 		if (!strcmp(cp, "cylinders")) {
1356 			if (GETNUM32(tp, &v) != 0) {
1357 				warnx("line %d: bad %s: %s", lineno, cp, tp);
1358 				errors++;
1359 			} else
1360 				lp->d_ncylinders = v;
1361 			continue;
1362 		}
1363 		if (!strcmp(cp, "total sectors")) {
1364 			if (GETNUM32(tp, &v) != 0) {
1365 				warnx("line %d: bad %s: %s", lineno, cp, tp);
1366 				errors++;
1367 			} else
1368 				lp->d_secperunit = v;
1369 			continue;
1370 		}
1371 		if (!strcmp(cp, "rpm")) {
1372 			if (GETNUM16(tp, &v) != 0) {
1373 				warnx("line %d: bad %s: %s", lineno, cp, tp);
1374 				errors++;
1375 			} else
1376 				lp->d_rpm = v;
1377 			continue;
1378 		}
1379 		if (!strcmp(cp, "interleave")) {
1380 			if (GETNUM16(tp, &v) != 0) {
1381 				warnx("line %d: bad %s: %s", lineno, cp, tp);
1382 				errors++;
1383 			} else
1384 				lp->d_interleave = v;
1385 			continue;
1386 		}
1387 		if (!strcmp(cp, "trackskew")) {
1388 			if (GETNUM16(tp, &v) != 0) {
1389 				warnx("line %d: bad %s: %s", lineno, cp, tp);
1390 				errors++;
1391 			} else
1392 				lp->d_trackskew = v;
1393 			continue;
1394 		}
1395 		if (!strcmp(cp, "cylinderskew")) {
1396 			if (GETNUM16(tp, &v) != 0) {
1397 				warnx("line %d: bad %s: %s", lineno, cp, tp);
1398 				errors++;
1399 			} else
1400 				lp->d_cylskew = v;
1401 			continue;
1402 		}
1403 		if (!strcmp(cp, "headswitch")) {
1404 			if (GETNUM32(tp, &v) != 0) {
1405 				warnx("line %d: bad %s: %s", lineno, cp, tp);
1406 				errors++;
1407 			} else
1408 				lp->d_headswitch = v;
1409 			continue;
1410 		}
1411 		if (!strcmp(cp, "track-to-track seek")) {
1412 			if (GETNUM32(tp, &v) != 0) {
1413 				warnx("line %d: bad %s: %s", lineno, cp, tp);
1414 				errors++;
1415 			} else
1416 				lp->d_trkseek = v;
1417 			continue;
1418 		}
1419 		if ('a' > *cp || *cp > 'z' || cp[1] != '\0') {
1420 			warnx("line %d: unknown field: %s", lineno, cp);
1421 			errors++;
1422 			continue;
1423 		}
1424 
1425 		/* We have a partition entry */
1426 		part = *cp - 'a';
1427 
1428 		if (part >= MAXPARTITIONS) {
1429 			warnx("line %d: bad partition name: %s", lineno, cp);
1430 			errors++;
1431 			continue;
1432 		}
1433 		pp = &lp->d_partitions[part];
1434 
1435 		NXTXNUM(pp->p_size);
1436 		NXTXNUM(pp->p_offset);
1437 		/* can't use word() here because of blanks in fstypenames[] */
1438 		tp += strspn(tp, " \t");
1439 		_CHECKLINE
1440 		cp = tp;
1441 		cpp = fstypenames;
1442 		for (; cpp < &fstypenames[FSMAXTYPES]; cpp++) {
1443 			s = *cpp;
1444 			if (s == NULL ||
1445 				(cp[strlen(s)] != ' ' &&
1446 				 cp[strlen(s)] != '\t' &&
1447 				 cp[strlen(s)] != '\0'))
1448 				continue;
1449 			if (!memcmp(s, cp, strlen(s))) {
1450 				pp->p_fstype = cpp - fstypenames;
1451 				tp += strlen(s);
1452 				if (*tp == '\0')
1453 					tp = NULL;
1454 				else {
1455 					tp += strspn(tp, " \t");
1456 					if (*tp == '\0')
1457 						tp = NULL;
1458 				}
1459 				goto gottype;
1460 			}
1461 		}
1462 		tp = word(cp);
1463 		if (isdigit(*cp & 0xff)) {
1464 			if (GETNUM8(cp, &v) != 0) {
1465 				warnx("line %d: syntax error", lineno);
1466 				errors++;
1467 			}
1468 		} else
1469 			v = FSMAXTYPES;
1470 		if ((unsigned)v >= FSMAXTYPES) {
1471 			warnx("line %d: warning, unknown filesystem type: %s",
1472 			    lineno, cp);
1473 			v = FS_UNUSED;
1474 		}
1475 		pp->p_fstype = v;
1476 gottype:
1477 		switch (pp->p_fstype) {
1478 
1479 		case FS_UNUSED:				/* XXX */
1480 			NXTNUM(pp->p_fsize);
1481 			if (pp->p_fsize == 0)
1482 				break;
1483 			NXTNUM(v);
1484 			pp->p_frag = v / pp->p_fsize;
1485 			break;
1486 
1487 		case FS_BSDFFS:
1488 		case FS_ADOS:
1489 		case FS_APPLEUFS:
1490 			NXTNUM(pp->p_fsize);
1491 			if (pp->p_fsize == 0)
1492 				break;
1493 			NXTNUM(v);
1494 			pp->p_frag = v / pp->p_fsize;
1495 			NXTNUM(pp->p_cpg);
1496 			break;
1497 		case FS_BSDLFS:
1498 			NXTNUM(pp->p_fsize);
1499 			if (pp->p_fsize == 0)
1500 				break;
1501 			NXTNUM(v);
1502 			pp->p_frag = v / pp->p_fsize;
1503 			NXTNUM(pp->p_sgs);
1504 			break;
1505 		case FS_EX2FS:
1506 			NXTNUM(pp->p_fsize);
1507 			if (pp->p_fsize == 0)
1508 				break;
1509 			NXTNUM(v);
1510 			pp->p_frag = v / pp->p_fsize;
1511 			break;
1512 		case FS_ISO9660:
1513 			NXTNUM(pp->p_cdsession);
1514 			break;
1515 		default:
1516 			break;
1517 		}
1518 		continue;
1519  error:
1520 		errors++;
1521  next:
1522 		;
1523 	}
1524 	errors += checklabel(lp);
1525 	return (errors == 0);
1526 }
1527 
1528 /*
1529  * Check disklabel for errors and fill in
1530  * derived fields according to supplied values.
1531  */
1532 int
1533 checklabel(struct disklabel *lp)
1534 {
1535 	struct partition *pp, *qp;
1536 	int	i, j, errors;
1537 	char	part;
1538 
1539 	errors = 0;
1540 	if (lp->d_secsize == 0) {
1541 		warnx("sector size %d", lp->d_secsize);
1542 		return (1);
1543 	}
1544 	if (lp->d_nsectors == 0) {
1545 		warnx("sectors/track %d", lp->d_nsectors);
1546 		return (1);
1547 	}
1548 	if (lp->d_ntracks == 0) {
1549 		warnx("tracks/cylinder %d", lp->d_ntracks);
1550 		return (1);
1551 	}
1552 	if  (lp->d_ncylinders == 0) {
1553 		warnx("cylinders/unit %d", lp->d_ncylinders);
1554 		errors++;
1555 	}
1556 	if (lp->d_rpm == 0)
1557 		warnx("warning, revolutions/minute %d", lp->d_rpm);
1558 	if (lp->d_secpercyl == 0)
1559 		lp->d_secpercyl = lp->d_nsectors * lp->d_ntracks;
1560 	if (lp->d_secperunit == 0)
1561 		lp->d_secperunit = lp->d_secpercyl * lp->d_ncylinders;
1562 #ifdef __i386__notyet__
1563 	if (dosdp && lp->d_secperunit > dosdp->mbrp_start + dosdp->mbrp_size) {
1564 		warnx("exceeds DOS partition size");
1565 		errors++;
1566 		lp->d_secperunit = dosdp->mbrp_start + dosdp->mbrp_size;
1567 	}
1568 	/* XXX should also check geometry against BIOS's idea */
1569 #endif	/* __i386__notyet__ */
1570 #ifdef __arm32__notyet__
1571 	/* XXX similar code as for i386 */
1572 #endif	/* __arm32__notyet__ */
1573 	if (lp->d_bbsize == 0) {
1574 		warnx("boot block size %d", lp->d_bbsize);
1575 		errors++;
1576 	} else if (lp->d_bbsize % lp->d_secsize)
1577 		warnx("warning, boot block size %% sector-size != 0");
1578 	if (lp->d_sbsize == 0) {
1579 		warnx("super block size %d", lp->d_sbsize);
1580 		errors++;
1581 	} else if (lp->d_sbsize % lp->d_secsize)
1582 		warnx("warning, super block size %% sector-size != 0");
1583 	if (lp->d_npartitions > MAXPARTITIONS)
1584 		warnx("warning, number of partitions (%d) > MAXPARTITIONS (%d)",
1585 		    lp->d_npartitions, MAXPARTITIONS);
1586 	else
1587 		for (i = MAXPARTITIONS - 1; i >= lp->d_npartitions; i--) {
1588 			part = 'a' + i;
1589 			pp = &lp->d_partitions[i];
1590 			if (pp->p_size || pp->p_offset) {
1591 				warnx("warning, partition %c increased "
1592 				    "number of partitions from %d to %d",
1593 				    part, lp->d_npartitions, i + 1);
1594 				lp->d_npartitions = i + 1;
1595 				break;
1596 			}
1597 		}
1598 	for (i = 0; i < lp->d_npartitions; i++) {
1599 		part = 'a' + i;
1600 		pp = &lp->d_partitions[i];
1601 		if (pp->p_size == 0 && pp->p_offset != 0)
1602 			warnx("warning, partition %c: size 0, but offset %d",
1603 			    part, pp->p_offset);
1604 #ifdef STRICT_CYLINDER_ALIGNMENT
1605 		if (pp->p_offset % lp->d_secpercyl) {
1606 			warnx("warning, partition %c:"
1607 			    " not starting on cylinder boundary",
1608 			    part);
1609 			errors++;
1610 		}
1611 #endif	/* STRICT_CYLINDER_ALIGNMENT */
1612 		if (pp->p_offset > lp->d_secperunit) {
1613 			warnx("partition %c: offset past end of unit", part);
1614 			errors++;
1615 		}
1616 		if (pp->p_offset + pp->p_size > lp->d_secperunit) {
1617 			warnx("partition %c: partition extends"
1618 			    " past end of unit",
1619 			    part);
1620 			errors++;
1621 		}
1622 		if (pp->p_fstype != FS_UNUSED)
1623 			for (j = i + 1; j < lp->d_npartitions; j++) {
1624 				qp = &lp->d_partitions[j];
1625 				if (qp->p_fstype == FS_UNUSED)
1626 					continue;
1627 				if (pp->p_offset < qp->p_offset + qp->p_size &&
1628 				    qp->p_offset < pp->p_offset + pp->p_size)
1629 					warnx("partitions %c and %c overlap",
1630 					    part, 'a' + j);
1631 			}
1632 	}
1633 	return (errors);
1634 }
1635 
1636 static void
1637 usage(void)
1638 {
1639 	static const struct {
1640 		const char *name;
1641 		const char *expn;
1642 	} usages[] = {
1643 	{ "[-CFrt] disk", "(to read label)" },
1644 	{ "-w [-Fr] [-f disktab] disk disktype [packid]", "(to write label)" },
1645 	{ "-e [-CFIr] disk", "(to edit label)" },
1646 	{ "-i [-FIr] disk", "(to create a label interactively)" },
1647 	{ "-R [-Fr] disk protofile", "(to restore label)" },
1648 	{ "[-NW] disk", "(to write disable/enable label)" },
1649 	{ NULL, NULL }
1650 	};
1651 	int i;
1652 	const char *pn = getprogname();
1653 	const char *t = "usage:";
1654 
1655 	for (i = 0; usages[i].name != NULL; i++) {
1656 		(void)fprintf(stderr, "%s %s %s\n\t%s\n",
1657 		    t, pn, usages[i].name, usages[i].expn);
1658 		t = "or";
1659 	}
1660 	exit(1);
1661 }
1662 
1663 static int
1664 getulong(const char *str, char sep, char **epp, unsigned long *ul,
1665     unsigned long max)
1666 {
1667 	char *ep;
1668 
1669 	if (epp == NULL)
1670 		epp = &ep;
1671 
1672 	*ul = strtoul(str, epp, 10);
1673 
1674 	if ((*ul ==  ULONG_MAX && errno == ERANGE) || *ul > max)
1675 		return ERANGE;
1676 
1677 	if (*str == '\0' || (**epp != '\0' && **epp != sep &&
1678 	    !isspace((unsigned char)**epp)))
1679 		return EFTYPE;
1680 
1681 	return 0;
1682 }
1683