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