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