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