xref: /openbsd-src/sbin/disklabel/disklabel.c (revision 2b0358df1d88d06ef4139321dd05bd5e05d91eaf)
1 /*	$OpenBSD: disklabel.c,v 1.147 2009/03/31 23:58:36 krw 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 #ifndef lint
36 static const char copyright[] =
37 "@(#) Copyright (c) 1987, 1993\n\
38 	The Regents of the University of California.  All rights reserved.\n";
39 #endif /* not lint */
40 
41 #ifndef lint
42 static const char rcsid[] = "$OpenBSD: disklabel.c,v 1.147 2009/03/31 23:58:36 krw Exp $";
43 #endif /* not lint */
44 
45 #include <sys/param.h>
46 #include <sys/ioctl.h>
47 #include <sys/stat.h>
48 #include <sys/wait.h>
49 #define DKTYPENAMES
50 #include <sys/disklabel.h>
51 
52 #include <ufs/ffs/fs.h>
53 
54 #include <ctype.h>
55 #include <err.h>
56 #include <errno.h>
57 #include <fcntl.h>
58 #include <limits.h>
59 #include <signal.h>
60 #include <string.h>
61 #include <stdio.h>
62 #include <stdlib.h>
63 #include <unistd.h>
64 #include <util.h>
65 #include "pathnames.h"
66 #include "extern.h"
67 
68 /*
69  * Disklabel: read and write disklabels.
70  * The label is usually placed on one of the first sectors of the disk.
71  * Many machines also place a bootstrap in the same area,
72  * in which case the label is embedded in the bootstrap.
73  * The bootstrap source must leave space at the proper offset
74  * for the label on such machines.
75  */
76 
77 #ifndef BBSIZE
78 #define	BBSIZE	8192			/* size of boot area, with label */
79 #endif
80 
81 #ifndef NUMBOOT
82 #define NUMBOOT 0
83 #endif
84 
85 char	*dkname, *specname;
86 char	tmpfil[] = _PATH_TMPFILE;
87 struct	disklabel lab;
88 char	bootarea[BBSIZE];
89 
90 #if NUMBOOT > 0
91 char	namebuf[BBSIZE], *np = namebuf;
92 int	installboot;	/* non-zero if we should install a boot program */
93 char	*bootbuf;	/* pointer to buffer with remainder of boot prog */
94 int	bootsize;	/* size of remaining boot program */
95 char	*xxboot;	/* primary boot */
96 char	*bootxx;	/* secondary boot */
97 char	boot0[MAXPATHLEN];
98 void	setbootflag(struct disklabel *);
99 #endif
100 
101 enum {
102 	UNSPEC, EDIT, EDITOR, READ, RESTORE, SETWRITEABLE, WRITE, WRITEBOOT
103 } op = UNSPEC;
104 
105 int	aflag;
106 int	cflag;
107 int	dflag;
108 int	tflag;
109 int	verbose;
110 int	donothing;
111 
112 #ifdef DOSLABEL
113 struct dos_partition *dosdp;	/* DOS partition, if found */
114 struct dos_partition *findopenbsd(int, off_t, struct dos_partition **, int *);
115 struct dos_partition *readmbr(int);
116 #endif
117 
118 #ifdef DPMELABEL
119 int	dpme_label = 0;
120 uint32_t dpme_obsd_start, dpme_obsd_size;
121 int	check_dpme(int, uint32_t *, uint32_t *);
122 #endif
123 
124 void	makedisktab(FILE *, struct disklabel *);
125 void	makelabel(char *, char *, struct disklabel *);
126 int	writelabel(int, char *, struct disklabel *);
127 void	l_perror(char *);
128 int	edit(struct disklabel *, int);
129 int	editit(const char *);
130 char	*skip(char *);
131 char	*word(char *);
132 int	getasciilabel(FILE *, struct disklabel *);
133 int	cmplabel(struct disklabel *, struct disklabel *);
134 void	usage(void);
135 u_int64_t getnum(char *, u_int64_t, u_int64_t, const char **);
136 
137 int
138 main(int argc, char *argv[])
139 {
140 	int ch, f, writeable, error = 0;
141 	char *fstabfile = NULL;
142 	struct disklabel *lp;
143 	char print_unit = 0;
144 	FILE *t;
145 
146 	while ((ch = getopt(argc, argv, "ABEf:NRWb:cdenp:s:tvw")) != -1)
147 		switch (ch) {
148 		case 'A':
149 			++aflag;
150 			break;
151 #if NUMBOOT > 0
152 		case 'B':
153 			++installboot;
154 			break;
155 		case 'b':
156 			xxboot = optarg;
157 			break;
158 #endif
159 		case 'N':
160 			if (op != UNSPEC)
161 				usage();
162 			writeable = 0;
163 			op = SETWRITEABLE;
164 			break;
165 		case 'R':
166 			if (op != UNSPEC)
167 				usage();
168 			op = RESTORE;
169 			break;
170 		case 'W':
171 			if (op != UNSPEC)
172 				usage();
173 			writeable = 1;
174 			op = SETWRITEABLE;
175 			break;
176 		case 'c':
177 			++cflag;
178 			break;
179 		case 'd':
180 			++dflag;
181 			break;
182 		case 'e':
183 			if (op != UNSPEC)
184 				usage();
185 			op = EDIT;
186 			break;
187 		case 'E':
188 			if (op != UNSPEC)
189 				usage();
190 			op = EDITOR;
191 			break;
192 		case 'f':
193 			fstabfile = optarg;
194 			break;
195 		case 't':
196 			++tflag;
197 			break;
198 		case 'w':
199 			if (op != UNSPEC)
200 				usage();
201 			op = WRITE;
202 			break;
203 		case 'p':
204 			if (strchr("bckmgt", optarg[0]) == NULL ||
205 			    optarg[1] != '\0')
206 				usage();
207 			print_unit = optarg[0];
208 			break;
209 		case 'n':
210 			donothing++;
211 			break;
212 		case 'v':
213 			verbose++;
214 			break;
215 		case '?':
216 		default:
217 			usage();
218 	}
219 	argc -= optind;
220 	argv += optind;
221 
222 #if NUMBOOT > 0
223 	if (installboot) {
224 		if (op == UNSPEC)
225 			op = WRITEBOOT;
226 	} else {
227 		if (op == UNSPEC)
228 			op = READ;
229 	}
230 #else
231 	if (op == UNSPEC)
232 		op = READ;
233 #endif
234 
235 	if (argc < 1 || (fstabfile && op != EDITOR))
236 		usage();
237 
238 	dkname = argv[0];
239 	f = opendev(dkname, (op == READ ? O_RDONLY : O_RDWR), OPENDEV_PART,
240 	    &specname);
241 	if (f < 0)
242 		err(4, "%s", specname);
243 
244 #ifdef DOSLABEL
245 	/*
246 	 * Check for presence of DOS partition table in
247 	 * master boot record. Return pointer to OpenBSD
248 	 * partition, if present. If no valid partition table,
249 	 * return NULL. If valid partition table present, but no
250 	 * partition to use, return a pointer to a non-386bsd
251 	 * partition.
252 	 */
253 	dosdp = readmbr(f);
254 #endif
255 
256 #ifdef DPMELABEL
257 	/*
258 	 * Check for a MacOS DPME partition table, and find out the
259 	 * area of the OpenBSD DPME partition, if any.
260 	 */
261 	dpme_label = check_dpme(f, &dpme_obsd_start, &dpme_obsd_size);
262 #endif
263 
264 	switch (op) {
265 	case EDIT:
266 		if (argc != 1)
267 			usage();
268 		if ((lp = readlabel(f)) == NULL)
269 			exit(1);
270 		error = edit(lp, f);
271 		break;
272 	case EDITOR:
273 		if (argc != 1)
274 			usage();
275 		if ((lp = readlabel(f)) == NULL)
276 			exit(1);
277 		error = editor(lp, f, specname, fstabfile, aflag);
278 		break;
279 	case READ:
280 		if (argc != 1)
281 			usage();
282 		if ((lp = readlabel(f)) == NULL)
283 			exit(1);
284 		if (tflag)
285 			makedisktab(stdout, lp);
286 		else
287 			display(stdout, lp, NULL, print_unit, 1);
288 		error = checklabel(lp);
289 		break;
290 	case RESTORE:
291 		if (argc < 2 || argc > 3)
292 			usage();
293 #if NUMBOOT > 0
294 		if (installboot && argc == 3)
295 			makelabel(argv[2], NULL, &lab);
296 #endif
297 		lp = makebootarea(bootarea, &lab, f);
298 		if (!(t = fopen(argv[1], "r")))
299 			err(4, "%s", argv[1]);
300 		if (getasciilabel(t, lp))
301 			error = writelabel(f, bootarea, lp);
302 		else
303 			error = 1;
304 		fclose(t);
305 		break;
306 	case SETWRITEABLE:
307 		if (!donothing) {
308 			if (ioctl(f, DIOCWLABEL, (char *)&writeable) < 0)
309 				err(4, "ioctl DIOCWLABEL");
310 		}
311 		break;
312 	case WRITE:
313 		if (dflag) {
314 			if (readlabel(f) == NULL)
315 				exit(1);
316 		} else if (argc < 2 || argc > 3)
317 			usage();
318 		else
319 			makelabel(argv[1], argc == 3 ? argv[2] : NULL, &lab);
320 		lp = makebootarea(bootarea, &lab, f);
321 		*lp = lab;
322 		if (checklabel(lp) == 0)
323 			error = writelabel(f, bootarea, lp);
324 		break;
325 #if NUMBOOT > 0
326 	case WRITEBOOT:
327 	{
328 		struct disklabel tlab;
329 
330 		if ((lp = readlabel(f)) == NULL)
331 			exit(1);
332 		tlab = *lp;
333 		if (argc == 2)
334 			makelabel(argv[1], NULL, &lab);
335 		lp = makebootarea(bootarea, &lab, f);
336 		*lp = tlab;
337 		if (checklabel(lp) == 0)
338 			error = writelabel(f, bootarea, lp);
339 		break;
340 	}
341 #endif
342 	default:
343 		break;
344 	}
345 	exit(error);
346 }
347 
348 /*
349  * Construct a prototype disklabel from /etc/disktab.  As a side
350  * effect, set the names of the primary and secondary boot files
351  * if specified.
352  */
353 void
354 makelabel(char *type, char *name, struct disklabel *lp)
355 {
356 	struct disklabel *dp;
357 
358 	dp = getdiskbyname(type);
359 	if (dp == NULL)
360 		errx(1, "unknown disk type: %s", type);
361 	*lp = *dp;
362 #if NUMBOOT > 0
363 	/*
364 	 * Set bootstrap name(s).
365 	 * 1. If set from command line, use those,
366 	 * 2. otherwise, check if disktab specifies them (b0 or b1),
367 	 * 3. otherwise, makebootarea() will choose ones based on the name
368 	 *    of the disk special file. E.g. /dev/ra0 -> raboot, bootra
369 	 */
370 	if (!xxboot && lp->d_boot0) {
371 		if (*lp->d_boot0 != '/')
372 			(void)snprintf(boot0, sizeof boot0, "%s%s",
373 			    _PATH_BOOTDIR, lp->d_boot0);
374 		else
375 			(void)strlcpy(boot0, lp->d_boot0, sizeof boot0);
376 		xxboot = boot0;
377 	}
378 #endif
379 	/* d_packname is union d_boot[01], so zero */
380 	memset(lp->d_packname, 0, sizeof(lp->d_packname));
381 	if (name)
382 		(void)strncpy(lp->d_packname, name, sizeof(lp->d_packname));
383 }
384 
385 
386 int
387 writelabel(int f, char *boot, struct disklabel *lp)
388 {
389 #if NUMBOOT > 0
390 	int writeable;
391 	off_t sectoffset = 0;
392 #endif
393 
394 #if NUMBOOT > 0
395 	setbootflag(lp);
396 #endif
397 	lp->d_magic = DISKMAGIC;
398 	lp->d_magic2 = DISKMAGIC;
399 	lp->d_checksum = 0;
400 	lp->d_checksum = dkcksum(lp);
401 #if NUMBOOT > 0
402 	if (installboot) {
403 #ifdef DOSLABEL
404 		struct partition *pp = &lp->d_partitions[2];
405 
406 		/*
407 		 * If OpenBSD DOS partition is missing, or if
408 		 * the label to be written is not within partition,
409 		 * prompt first. Need to allow this in case operator
410 		 * wants to convert the drive for dedicated use.
411 		 * In this case, partition 'a' had better start at 0,
412 		 * otherwise we reject the request as meaningless. -wfj
413 		 */
414 		if (dosdp && DL_GETPSIZE(pp) && (dosdp->dp_typ == DOSPTYP_OPENBSD)) {
415 			sectoffset = (off_t)letoh32(dosdp->dp_start) *
416 			    lp->d_secsize;
417 		} else {
418 			if (dosdp) {
419 				int first, ch;
420 
421 				printf("Erase the previous contents of the disk? [n]: ");
422 				fflush(stdout);
423 				first = ch = getchar();
424 				while (ch != '\n' && ch != EOF)
425 					ch = getchar();
426 				if (first != 'y' && first != 'Y')
427 					exit(0);
428 			}
429 			sectoffset = 0;
430 		}
431 #endif
432 		/*
433 		 * First set the kernel disk label,
434 		 * then write a label to the raw disk.
435 		 * If the SDINFO ioctl fails because it is unimplemented,
436 		 * keep going; otherwise, the kernel consistency checks
437 		 * may prevent us from changing the current (in-core)
438 		 * label.
439 		 */
440 		if (!donothing) {
441 			if (ioctl(f, DIOCSDINFO, lp) < 0 &&
442 			    errno != ENODEV && errno != ENOTTY) {
443 				l_perror("ioctl DIOCSDINFO");
444 				return (1);
445 			}
446 		}
447 		if (verbose)
448 			printf("writing label to block %lld (0x%qx)\n",
449 			    (long long)sectoffset/DEV_BSIZE,
450 			    (long long)sectoffset/DEV_BSIZE);
451 		if (!donothing) {
452 			if (lseek(f, sectoffset, SEEK_SET) < 0) {
453 				perror("lseek");
454 				return (1);
455 			}
456 			/*
457 			 * write enable label sector before write (if necessary),
458 			 * disable after writing.
459 			 */
460 			writeable = 1;
461 
462 			if (ioctl(f, DIOCWLABEL, &writeable) < 0)
463 				perror("ioctl DIOCWLABEL");
464 			if (write(f, boot, lp->d_bbsize) != lp->d_bbsize) {
465 				perror("write");
466 				return (1);
467 			}
468 		}
469 		/*
470 		 * Output the remainder of the disklabel
471 		 */
472 		if (!donothing && bootbuf && write(f, bootbuf, bootsize) != bootsize) {
473 			perror("write");
474 			return(1);
475 		}
476 		writeable = 0;
477 		if (!donothing)
478 			if (ioctl(f, DIOCWLABEL, &writeable) < 0)
479 				perror("ioctl DIOCWLABEL");
480 	} else
481 #endif /* NUMBOOT > 0 */
482 	if (!donothing) {
483 		if (ioctl(f, DIOCWDINFO, lp) < 0) {
484 			l_perror("ioctl DIOCWDINFO");
485 			return (1);
486 		}
487 	}
488 #ifdef __vax__
489 	if (lp->d_type == DTYPE_SMD && lp->d_flags & D_BADSECT) {
490 		off_t alt;
491 		int i;
492 
493 		alt = lp->d_ncylinders * lp->d_secpercyl - lp->d_nsectors;
494 		for (i = 1; i < 11 && i < lp->d_nsectors; i += 2) {
495 			(void)lseek(f, (alt + i) * lp->d_secsize, SEEK_SET);
496 			if (!donothing)
497 				if (write(f, boot, lp->d_secsize) != lp->d_secsize)
498 					warn("alternate label %d write", i/2);
499 		}
500 	}
501 #endif
502 	return (0);
503 }
504 
505 void
506 l_perror(char *s)
507 {
508 
509 	switch (errno) {
510 	case ESRCH:
511 		warnx("%s: No disk label on disk", s);
512 		break;
513 	case EINVAL:
514 		warnx("%s: Label magic number or checksum is wrong!\n"
515 		    "(disklabel or kernel is out of date?)", s);
516 		break;
517 	case EBUSY:
518 		warnx("%s: Open partition would move or shrink", s);
519 		break;
520 	case EXDEV:
521 		warnx("%s: Labeled partition or 'a' partition must start "
522 		    "at beginning of disk", s);
523 		break;
524 	default:
525 		warn("%s", s);
526 		break;
527 	}
528 }
529 
530 #ifdef DOSLABEL
531 struct dos_partition *
532 findopenbsd(int f, off_t mbroff, struct dos_partition **first, int *n)
533 {
534 	static struct dos_partition res;
535 	int mbr[DEV_BSIZE / sizeof(int)];
536 	struct dos_partition *dp, *p;
537 	u_int16_t signature;
538 	u_int32_t start = 0;
539 	int part;
540 
541 	/* Limit the number of recursions */
542 	if (!(*n)--)
543 		return (NULL);
544 
545 	/*
546 	 * This must be done this way due to alignment restrictions
547 	 * in for example mips processors.
548 	 */
549 	dp = (struct dos_partition *)mbr;
550 	if (lseek(f, (off_t)mbroff * DEV_BSIZE, SEEK_SET) < 0 ||
551 	    read(f, mbr, sizeof(mbr)) != sizeof(mbr))
552 		return (NULL);
553 	signature = *((u_char *)mbr + DOSMBR_SIGNATURE_OFF) |
554 	    (*((u_char *)mbr + DOSMBR_SIGNATURE_OFF + 1) << 8);
555 	bcopy((char *)mbr+DOSPARTOFF, (char *)mbr, sizeof(*dp) * NDOSPART);
556 
557 	/*
558 	 * If there is no signature and no OpenBSD partition this is probably
559 	 * not an MBR.
560 	 */
561 	if (signature != DOSMBR_SIGNATURE)
562 		return (NULL);
563 
564 	/*
565 	 * Don't (yet) know disk geometry, use partition table to find OpenBSD
566 	 * partition, and obtain disklabel from there.
567 	 */
568 	/* Check if table is valid. */
569 	for (part = 0; part < NDOSPART; part++) {
570 		if ((dp[part].dp_flag & ~0x80) != 0)
571 			return (NULL);
572 	}
573 	/* Find OpenBSD partition. */
574 	for (part = 0; part < NDOSPART; part++) {
575 		if (!letoh32(dp[part].dp_size))
576 			continue;
577 		if (first && *first == NULL) {
578 			bcopy(&dp[part], &res, sizeof(struct dos_partition));
579 			*first = &res;
580 		}
581 		switch (dp[part].dp_typ) {
582 		case DOSPTYP_OPENBSD:
583 			fprintf(stderr, "# Inside MBR partition %d: "
584 			    "type %02X start %u size %u\n",
585 			    part, dp[part].dp_typ,
586 			    letoh32(dp[part].dp_start), letoh32(dp[part].dp_size));
587 			bcopy(&dp[part], &res, sizeof(struct dos_partition));
588 			res.dp_start =
589 			    htole32((off_t)letoh32(res.dp_start) + mbroff);
590 			return (&res);
591 		case DOSPTYP_EXTEND:
592 		case DOSPTYP_EXTENDL:
593 			fprintf(stderr, "# Extended partition %d: "
594 			    "type %02X start %u size %u\n",
595 			    part, dp[part].dp_typ,
596 			    letoh32(dp[part].dp_start), letoh32(dp[part].dp_size));
597 			start = letoh32(dp[part].dp_start) + mbroff;
598 			p = findopenbsd(f, start, NULL, n);
599 			if (p != NULL)
600 				return (p);
601 			break;
602 		}
603 	}
604 
605 	return (NULL);
606 }
607 
608 /*
609  * Fetch DOS partition table from disk.
610  */
611 struct dos_partition *
612 readmbr(int f)
613 {
614 	struct dos_partition *dp, *first = NULL;
615 	int n = 8;
616 
617 	dp = findopenbsd(f, DOSBBSECTOR, &first, &n);
618 	if (dp != NULL)
619 		return (dp);
620 
621 	/* If no OpenBSD partition, find first used partition. */
622 	if (first != NULL) {
623 		warnx("warning, DOS partition table with no valid OpenBSD partition");
624 		return (first);
625 	}
626 
627 	/* Table appears to be empty. */
628 	return (NULL);
629 }
630 #endif
631 
632 #ifdef DPMELABEL
633 int
634 check_dpme(int f, uint32_t *start, uint32_t *size)
635 {
636 	char sector[DEV_BSIZE];
637 	unsigned int partno, partcnt;
638 	struct part_map_entry part;
639 
640 	/*
641 	 * Read what would be the first DPME partition, and
642 	 * check for a valid signature.
643 	 */
644 
645 	if (lseek(f, (off_t)DEV_BSIZE, SEEK_SET) < 0 ||
646 	    read(f, sector, sizeof(sector)) != sizeof(sector))
647 		return (0);
648 	/* no direct derefence due to strict alignment */
649 	memcpy(&part, sector, sizeof part);
650 
651 	if (part.pmSig != PART_ENTRY_MAGIC)
652 		return (0);
653 
654 	/*
655 	 * If the signature matches, perform a few more sanity checks,
656 	 * and then loop over the remaining partitions.  We can safely
657 	 * rely on the first entry being of the partition map type,
658 	 * so it's ok to skip it.
659 	 */
660 
661 	partcnt = part.pmMapBlkCnt;
662 	if (partcnt <= 1 || partcnt > 32)
663 		return (0);
664 
665 	for (partno = 2; partno <= partcnt; partno++) {
666 		if (lseek(f, (off_t)partno * DEV_BSIZE, SEEK_SET) < 0 ||
667 		    read(f, sector, sizeof(sector)) != sizeof(sector))
668 			return (0);
669 		/* no direct derefence due to strict alignment */
670 		memcpy(&part, sector, sizeof part);
671 
672 		if (part.pmSig != PART_ENTRY_MAGIC)
673 			return (0);
674 
675 		if (strcasecmp(part.pmPartType, PART_TYPE_OPENBSD) == 0) {
676 			*start = part.pmPyPartStart;
677 			*size = part.pmPartBlkCnt;
678 			return (1);
679 		}
680 	}
681 
682 	return (0);
683 }
684 #endif
685 
686 /*
687  * Fetch disklabel for disk using ioctl.
688  */
689 struct disklabel *
690 readlabel(int f)
691 {
692 	struct disklabel *lp = NULL;
693 
694 	if (cflag && ioctl(f, DIOCRLDINFO) < 0)
695 		err(4, "ioctl DIOCRLDINFO");
696 	if (dflag) {
697 		lp = &lab;
698 		if (ioctl(f, DIOCGPDINFO, lp) < 0)
699 			err(4, "ioctl DIOCGPDINFO");
700 	} else {
701 		lp = &lab;
702 		if (ioctl(f, DIOCGDINFO, lp) < 0)
703 			err(4, "ioctl DIOCGDINFO");
704 	}
705 
706 	return (lp);
707 }
708 
709 /*
710  * Construct a bootarea (d_bbsize bytes) in the specified buffer ``boot''
711  * Returns a pointer to the disklabel portion of the bootarea.
712  */
713 struct disklabel *
714 makebootarea(char *boot, struct disklabel *dp, int f)
715 {
716 	struct disklabel *lp;
717 	char *p;
718 #if NUMBOOT > 0
719 	char *dkbasename;
720 	int b;
721 	struct stat sb;
722 #endif
723 
724 	/* XXX */
725 	if (dp->d_secsize == 0) {
726 		dp->d_secsize = DEV_BSIZE;
727 		dp->d_bbsize = BBSIZE;
728 	}
729 	lp = (struct disklabel *)
730 	    (boot + (LABELSECTOR * dp->d_secsize) + LABELOFFSET);
731 	memset(lp, 0, sizeof *lp);
732 #if NUMBOOT > 0
733 	/*
734 	 * If we are not installing a boot program but we are installing a
735 	 * label on disk then we must read the current bootarea so we don't
736 	 * clobber the existing boot.
737 	 */
738 	if (!installboot)
739 		return (lp);
740 	/*
741 	 * We are installing a boot program.  Determine the name(s) and
742 	 * read them into the appropriate places in the boot area.
743 	 */
744 	if (!xxboot || !bootxx) {
745 		dkbasename = np;
746 		if ((p = strrchr(dkname, '/')) == NULL)
747 			p = dkname;
748 		else
749 			p++;
750 		while (*p && !isdigit(*p))
751 			*np++ = *p++;
752 		*np++ = '\0';
753 
754 		if (!xxboot) {
755 			(void)snprintf(np, namebuf + sizeof namebuf - np,
756 			    "%s%sboot", _PATH_BOOTDIR, dkbasename);
757 			if (access(np, F_OK) < 0 && dkbasename[0] == 'r')
758 				dkbasename++;
759 			xxboot = np;
760 			(void)snprintf(xxboot,
761 			    namebuf + sizeof namebuf - np,
762 			    "%s%sboot", _PATH_BOOTDIR, dkbasename);
763 			np += strlen(xxboot) + 1;
764 		}
765 	}
766 	if (verbose)
767 		warnx("bootstraps: xxboot = %s, bootxx = %s", xxboot,
768 		    bootxx ? bootxx : "NONE");
769 
770 	/*
771 	 * For NUMBOOT > 0 architectures (hp300/hppa/hppa64/landisk/vax)
772 	 * up to d_bbsize bytes of ``xxboot'' go in bootarea, the rest
773 	 * is remembered and written later following the bootarea.
774 	 */
775 	b = open(xxboot, O_RDONLY);
776 	if (b < 0)
777 		err(4, "%s", xxboot);
778 	if (read(b, boot, (int)dp->d_bbsize) < 0)
779 		err(4, "%s", xxboot);
780 	(void)fstat(b, &sb);
781 	bootsize = (int)sb.st_size - dp->d_bbsize;
782 	if (bootsize > 0) {
783 		/* XXX assume d_secsize is a power of two */
784 		bootsize = (bootsize + dp->d_secsize-1) & ~(dp->d_secsize-1);
785 		bootbuf = (char *)malloc((size_t)bootsize);
786 		if (bootbuf == NULL)
787 			err(4, "%s", xxboot);
788 		if (read(b, bootbuf, bootsize) < 0) {
789 			free(bootbuf);
790 			err(4, "%s", xxboot);
791 		}
792 	}
793 	(void)close(b);
794 #endif
795 	/*
796 	 * Make sure no part of the bootstrap is written in the area
797 	 * reserved for the label.
798 	 */
799 	for (p = (char *)lp; p < (char *)lp + sizeof(struct disklabel); p++)
800 		if (*p)
801 			errx(2, "Bootstrap doesn't leave room for disk label");
802 	return (lp);
803 }
804 
805 void
806 makedisktab(FILE *f, struct disklabel *lp)
807 {
808 	int i;
809 	char *did = "\\\n\t:";
810 	struct partition *pp;
811 
812 	if (lp->d_packname[0])
813 		(void)fprintf(f, "%.*s|", (int)sizeof(lp->d_packname),
814 		    lp->d_packname);
815 	if (lp->d_typename[0])
816 		(void)fprintf(f, "%.*s|", (int)sizeof(lp->d_typename),
817 		    lp->d_typename);
818 	(void)fputs("Automatically generated label:\\\n\t:dt=", f);
819 	if ((unsigned) lp->d_type < DKMAXTYPES)
820 		(void)fprintf(f, "%s:", dktypenames[lp->d_type]);
821 	else
822 		(void)fprintf(f, "unknown%d:", lp->d_type);
823 
824 	(void)fprintf(f, "se#%u:", lp->d_secsize);
825 	(void)fprintf(f, "ns#%u:", lp->d_nsectors);
826 	(void)fprintf(f, "nt#%u:", lp->d_ntracks);
827 	(void)fprintf(f, "nc#%u:", lp->d_ncylinders);
828 	(void)fprintf(f, "sc#%u:", lp->d_secpercyl);
829 	(void)fprintf(f, "su#%llu:", DL_GETDSIZE(lp));
830 
831 	if (lp->d_rpm != 3600) {
832 		(void)fprintf(f, "%srm#%hu:", did, lp->d_rpm);
833 		did = "";
834 	}
835 	if (lp->d_interleave != 1) {
836 		(void)fprintf(f, "%sil#%hu:", did, lp->d_interleave);
837 		did = "";
838 	}
839 	if (lp->d_trackskew != 0) {
840 		(void)fprintf(f, "%ssk#%hu:", did, lp->d_trackskew);
841 		did = "";
842 	}
843 	if (lp->d_cylskew != 0) {
844 		(void)fprintf(f, "%scs#%hu:", did, lp->d_cylskew);
845 		did = "";
846 	}
847 	if (lp->d_headswitch != 0) {
848 		(void)fprintf(f, "%shs#%u:", did, lp->d_headswitch);
849 		did = "";
850 	}
851 	if (lp->d_trkseek != 0) {
852 		(void)fprintf(f, "%sts#%u:", did, lp->d_trkseek);
853 		did = "";
854 	}
855 	for (i = 0; i < NDDATA; i++)
856 		if (lp->d_drivedata[i])
857 			(void)fprintf(f, "d%d#%u", i, lp->d_drivedata[i]);
858 	pp = lp->d_partitions;
859 	for (i = 0; i < lp->d_npartitions; i++, pp++) {
860 		if (DL_GETPSIZE(pp)) {
861 			char c = 'a' + i;
862 
863 			(void)fprintf(f, "\\\n\t:");
864 			(void)fprintf(f, "p%c#%llu:", c, DL_GETPSIZE(pp));
865 			(void)fprintf(f, "o%c#%llu:", c, DL_GETPOFFSET(pp));
866 			if (pp->p_fstype != FS_UNUSED) {
867 				if ((unsigned) pp->p_fstype < FSMAXTYPES)
868 					(void)fprintf(f, "t%c=%s:", c,
869 					    fstypenames[pp->p_fstype]);
870 				else
871 					(void)fprintf(f, "t%c=unknown%d:",
872 					    c, pp->p_fstype);
873 			}
874 			switch (pp->p_fstype) {
875 
876 			case FS_UNUSED:
877 				break;
878 
879 			case FS_BSDFFS:
880 				(void)fprintf(f, "b%c#%u:", c,
881 				    DISKLABELV1_FFS_BSIZE(pp->p_fragblock));
882 				(void)fprintf(f, "f%c#%u:", c,
883 				    DISKLABELV1_FFS_FSIZE(pp->p_fragblock));
884 				break;
885 
886 			default:
887 				break;
888 			}
889 		}
890 	}
891 	(void)fputc('\n', f);
892 	(void)fflush(f);
893 }
894 
895 double
896 scale(u_int64_t sz, char unit, struct disklabel *lp)
897 {
898 	double fsz;
899 
900 	fsz = (double)sz * lp->d_secsize;
901 
902 	switch (unit) {
903 	case 'B':
904 		return fsz;
905 	case 'C':
906 		return fsz / lp->d_secsize / lp->d_secpercyl;
907 	case 'K':
908 		return fsz / 1024;
909 	case 'M':
910 		return fsz / (1024 * 1024);
911 	case 'G':
912 		return fsz / (1024 * 1024 * 1024);
913 	case 'T':
914 		return fsz / (1024ULL * 1024 * 1024 * 1024);
915 	default:
916 		return -1.0;
917 	}
918 }
919 
920 /*
921  * Display a particular partition.
922  */
923 void
924 display_partition(FILE *f, struct disklabel *lp, char **mp, int i,
925     char unit)
926 {
927 	volatile struct partition *pp = &lp->d_partitions[i];
928 	double p_size, p_offset;
929 
930 	p_size = scale(DL_GETPSIZE(pp), unit, lp);
931 	p_offset = scale(DL_GETPOFFSET(pp), unit, lp);
932 	if (DL_GETPSIZE(pp)) {
933 		u_int32_t frag = DISKLABELV1_FFS_FRAG(pp->p_fragblock);
934 		u_int32_t fsize = DISKLABELV1_FFS_FSIZE(pp->p_fragblock);
935 
936 		if (p_size < 0)
937 			fprintf(f, "  %c: %16llu %16llu ", 'a' + i,
938 			    DL_GETPSIZE(pp), DL_GETPOFFSET(pp));
939 		else
940 			fprintf(f, "  %c: %15.*f%c %15.*f%c ", 'a' + i,
941 			    unit == 'B' ? 0 : 1, p_size, unit,
942 			    unit == 'B' ? 0 : 1, p_offset, unit);
943 		if ((unsigned) pp->p_fstype < FSMAXTYPES)
944 			fprintf(f, "%7.7s", fstypenames[pp->p_fstype]);
945 		else
946 			fprintf(f, "%7d", pp->p_fstype);
947 
948 		switch (pp->p_fstype) {
949 		case FS_BSDFFS:
950 			fprintf(f, "  %5u %5u %4hu ",
951 			    fsize, fsize * frag,
952 			    pp->p_cpg);
953 			break;
954 		default:
955 			fprintf(f, "%19.19s", "");
956 			break;
957 		}
958 
959 		if (mp != NULL) {
960 			if (mp[i] != NULL)
961 				fprintf(f, "# %s", mp[i]);
962 		}
963 		putc('\n', f);
964 	}
965 }
966 
967 void
968 display(FILE *f, struct disklabel *lp, char **mp, char unit, int all)
969 {
970 	int i, j;
971 	double d;
972 
973 	unit = toupper(unit);
974 	fprintf(f, "# %s:\n", specname);
975 
976 	if ((unsigned) lp->d_type < DKMAXTYPES)
977 		fprintf(f, "type: %s\n", dktypenames[lp->d_type]);
978 	else
979 		fprintf(f, "type: %d\n", lp->d_type);
980 	fprintf(f, "disk: %.*s\n", (int)sizeof(lp->d_typename), lp->d_typename);
981 	fprintf(f, "label: %.*s\n", (int)sizeof(lp->d_packname), lp->d_packname);
982 	fprintf(f, "flags:");
983 	if (lp->d_flags & D_BADSECT)
984 		fprintf(f, " badsect");
985 	if (lp->d_flags & D_VENDOR)
986 		fprintf(f, " vendor");
987 	putc('\n', f);
988 
989 	fprintf(f, "bytes/sector: %u\n", lp->d_secsize);
990 	fprintf(f, "sectors/track: %u\n", lp->d_nsectors);
991 	fprintf(f, "tracks/cylinder: %u\n", lp->d_ntracks);
992 	fprintf(f, "sectors/cylinder: %u\n", lp->d_secpercyl);
993 	fprintf(f, "cylinders: %u\n", lp->d_ncylinders);
994 	d = scale(DL_GETDSIZE(lp), unit, lp);
995 	if (d < 0)
996 		fprintf(f, "total sectors: %llu\n", DL_GETDSIZE(lp));
997 	else
998 		fprintf(f, "total bytes: %.*f%c\n", unit == 'B' ? 0 : 1,
999 		    d, unit);
1000 
1001 	fprintf(f, "rpm: %hu\n", lp->d_rpm);
1002 	fprintf(f, "interleave: %hu\n", lp->d_interleave);
1003 	fprintf(f, "trackskew: %hu\n", lp->d_trackskew);
1004 	fprintf(f, "cylinderskew: %hu\n", lp->d_cylskew);
1005 	fprintf(f, "headswitch: %u\t\t# microseconds\n",
1006 	    lp->d_headswitch);
1007 	fprintf(f, "track-to-track seek: %u\t# microseconds\n",
1008 	    lp->d_trkseek);
1009 	fprintf(f, "drivedata: ");
1010 	for (i = NDDATA - 1; i >= 0; i--)
1011 		if (lp->d_drivedata[i])
1012 			break;
1013 	if (i < 0)
1014 		i = 0;
1015 	for (j = 0; j <= i; j++)
1016 		fprintf(f, "%d ", lp->d_drivedata[j]);
1017 	fprintf(f, "\n");
1018 	if (all) {
1019 		fprintf(f, "\n%hu partitions:\n", lp->d_npartitions);
1020 		fprintf(f, "#    %16.16s %16.16s  fstype [fsize bsize  cpg]\n",
1021 		    "size", "offset");
1022 		for (i = 0; i < lp->d_npartitions; i++)
1023 			display_partition(f, lp, mp, i, unit);
1024 	}
1025 	fflush(f);
1026 }
1027 
1028 int
1029 edit(struct disklabel *lp, int f)
1030 {
1031 	int first, ch, fd;
1032 	struct disklabel label;
1033 	FILE *fp;
1034 
1035 	if ((fd = mkstemp(tmpfil)) == -1 || (fp = fdopen(fd, "w")) == NULL) {
1036 		if (fd != -1)
1037 			close(fd);
1038 		warn("%s", tmpfil);
1039 		return (1);
1040 	}
1041 	display(fp, lp, NULL, 0, 1);
1042 	fprintf(fp, "\n# Notes:\n");
1043 	fprintf(fp,
1044 "# Up to 16 partitions are valid, named from 'a' to 'p'.  Partition 'a' is\n"
1045 "# your root filesystem, 'b' is your swap, and 'c' should cover your whole\n"
1046 "# disk. Any other partition is free for any use.  'size' and 'offset' are\n"
1047 "# in 512-byte blocks. fstype should be '4.2BSD', 'swap', or 'none' or some\n"
1048 "# other values.  fsize/bsize/cpg should typically be '2048 16384 16' for a\n"
1049 "# 4.2BSD filesystem (or '512 4096 16' except on alpha, sun4, ...)\n");
1050 	fclose(fp);
1051 	for (;;) {
1052 		if (editit(tmpfil) == -1)
1053 			break;
1054 		fp = fopen(tmpfil, "r");
1055 		if (fp == NULL) {
1056 			warn("%s", tmpfil);
1057 			break;
1058 		}
1059 		memset(&label, 0, sizeof(label));
1060 		if (getasciilabel(fp, &label)) {
1061 			if (cmplabel(lp, &label) == 0) {
1062 				puts("No changes.");
1063 				fclose(fp);
1064 				(void) unlink(tmpfil);
1065 				return (0);
1066 			}
1067 			*lp = label;
1068 			if (writelabel(f, bootarea, lp) == 0) {
1069 				fclose(fp);
1070 				(void) unlink(tmpfil);
1071 				return (0);
1072 			}
1073 		}
1074 		fclose(fp);
1075 		printf("re-edit the label? [y]: ");
1076 		fflush(stdout);
1077 		first = ch = getchar();
1078 		while (ch != '\n' && ch != EOF)
1079 			ch = getchar();
1080 		if (first == 'n' || first == 'N')
1081 			break;
1082 	}
1083 	(void)unlink(tmpfil);
1084 	return (1);
1085 }
1086 
1087 /*
1088  * Execute an editor on the specified pathname, which is interpreted
1089  * from the shell.  This means flags may be included.
1090  *
1091  * Returns -1 on error, or the exit value on success.
1092  */
1093 int
1094 editit(const char *pathname)
1095 {
1096 	char *argp[] = {"sh", "-c", NULL, NULL}, *ed, *p;
1097 	sig_t sighup, sigint, sigquit, sigchld;
1098 	pid_t pid;
1099 	int saved_errno, st, ret = -1;
1100 
1101 	ed = getenv("VISUAL");
1102 	if (ed == NULL || ed[0] == '\0')
1103 		ed = getenv("EDITOR");
1104 	if (ed == NULL || ed[0] == '\0')
1105 		ed = _PATH_VI;
1106 	if (asprintf(&p, "%s %s", ed, pathname) == -1)
1107 		return (-1);
1108 	argp[2] = p;
1109 
1110 	sighup = signal(SIGHUP, SIG_IGN);
1111 	sigint = signal(SIGINT, SIG_IGN);
1112 	sigquit = signal(SIGQUIT, SIG_IGN);
1113 	sigchld = signal(SIGCHLD, SIG_DFL);
1114 	if ((pid = fork()) == -1)
1115 		goto fail;
1116 	if (pid == 0) {
1117 		execv(_PATH_BSHELL, argp);
1118 		_exit(127);
1119 	}
1120 	while (waitpid(pid, &st, 0) == -1)
1121 		if (errno != EINTR)
1122 			goto fail;
1123 	if (!WIFEXITED(st))
1124 		errno = EINTR;
1125 	else
1126 		ret = WEXITSTATUS(st);
1127 
1128  fail:
1129 	saved_errno = errno;
1130 	(void)signal(SIGHUP, sighup);
1131 	(void)signal(SIGINT, sigint);
1132 	(void)signal(SIGQUIT, sigquit);
1133 	(void)signal(SIGCHLD, sigchld);
1134 	free(p);
1135 	errno = saved_errno;
1136 	return (ret);
1137 }
1138 
1139 char *
1140 skip(char *cp)
1141 {
1142 
1143 	cp += strspn(cp, " \t");
1144 	if (*cp == '\0')
1145 		return (NULL);
1146 	return (cp);
1147 }
1148 
1149 char *
1150 word(char *cp)
1151 {
1152 
1153 	cp += strcspn(cp, " \t");
1154 	if (*cp == '\0')
1155 		return (NULL);
1156 	*cp++ = '\0';
1157 	cp += strspn(cp, " \t");
1158 	if (*cp == '\0')
1159 		return (NULL);
1160 	return (cp);
1161 }
1162 
1163 /* Base the max value on the sizeof of the value we are reading */
1164 #define GETNUM(field, nptr, min, errstr)				\
1165 	    getnum((nptr), (min),					\
1166 		sizeof(field) == 8 ? LLONG_MAX :			\
1167 		(sizeof(field) == 4 ? UINT_MAX :			\
1168 		(sizeof(field) == 2 ? USHRT_MAX : UCHAR_MAX)),  (errstr))
1169 
1170 u_int64_t
1171 getnum(char *nptr, u_int64_t min, u_int64_t max, const char **errstr)
1172 {
1173 	char *p, c;
1174 	u_int64_t ret;
1175 
1176 	for (p = nptr; *p != '\0' && !isspace(*p); p++)
1177 		;
1178 	c = *p;
1179 	*p = '\0';
1180 	ret = strtonum(nptr, min, max, errstr);
1181 	*p = c;
1182 	return (ret);
1183 }
1184 
1185 /*
1186  * Read an ascii label in from FILE f,
1187  * in the same format as that put out by display(),
1188  * and fill in lp.
1189  */
1190 int
1191 getasciilabel(FILE *f, struct disklabel *lp)
1192 {
1193 	char **cpp, *cp;
1194 	const char *errstr;
1195 	struct partition *pp;
1196 	char *tp, *s, line[BUFSIZ];
1197 	int lineno = 0, errors = 0;
1198 	u_int32_t v, fsize;
1199 	u_int64_t lv;
1200 
1201 	lp->d_version = 1;
1202 	lp->d_bbsize = BBSIZE;				/* XXX */
1203 	lp->d_sbsize = SBSIZE;				/* XXX */
1204 	while (fgets(line, sizeof(line), f)) {
1205 		lineno++;
1206 		if ((cp = strpbrk(line, "#\r\n")))
1207 			*cp = '\0';
1208 		cp = skip(line);
1209 		if (cp == NULL)
1210 			continue;
1211 		tp = strchr(cp, ':');
1212 		if (tp == NULL) {
1213 			warnx("line %d: syntax error", lineno);
1214 			errors++;
1215 			continue;
1216 		}
1217 		*tp++ = '\0', tp = skip(tp);
1218 		if (!strcmp(cp, "type")) {
1219 			if (tp == NULL)
1220 				tp = "unknown";
1221 			else if (strcasecmp(tp, "IDE") == 0)
1222 				tp = "ESDI";
1223 			cpp = dktypenames;
1224 			for (; cpp < &dktypenames[DKMAXTYPES]; cpp++)
1225 				if ((s = *cpp) && !strcasecmp(s, tp)) {
1226 					lp->d_type = cpp - dktypenames;
1227 					goto next;
1228 				}
1229 			v = GETNUM(lp->d_type, tp, 0, &errstr);
1230 			if (errstr || v >= DKMAXTYPES)
1231 				warnx("line %d: warning, unknown disk type: %s",
1232 				    lineno, tp);
1233 			lp->d_type = v;
1234 			continue;
1235 		}
1236 		if (!strcmp(cp, "flags")) {
1237 			for (v = 0; (cp = tp) && *cp != '\0';) {
1238 				tp = word(cp);
1239 				if (!strcmp(cp, "badsect"))
1240 					v |= D_BADSECT;
1241 				else if (!strcmp(cp, "vendor"))
1242 					v |= D_VENDOR;
1243 				else {
1244 					warnx("line %d: bad flag: %s",
1245 					    lineno, cp);
1246 					errors++;
1247 				}
1248 			}
1249 			lp->d_flags = v;
1250 			continue;
1251 		}
1252 		if (!strcmp(cp, "drivedata")) {
1253 			int i;
1254 
1255 			for (i = 0; (cp = tp) && *cp != '\0' && i < NDDATA;) {
1256 				v = GETNUM(lp->d_drivedata[i], cp, 0, &errstr);
1257 				if (errstr)
1258 					warnx("line %d: bad drivedata %s",
1259 					   lineno, cp);
1260 				lp->d_drivedata[i++] = v;
1261 				tp = word(cp);
1262 			}
1263 			continue;
1264 		}
1265 		if (sscanf(cp, "%d partitions", &v) == 1) {
1266 			if (v == 0 || v > MAXPARTITIONS) {
1267 				warnx("line %d: bad # of partitions", lineno);
1268 				lp->d_npartitions = MAXPARTITIONS;
1269 				errors++;
1270 			} else
1271 				lp->d_npartitions = v;
1272 			continue;
1273 		}
1274 		if (tp == NULL)
1275 			tp = "";
1276 		if (!strcmp(cp, "disk")) {
1277 			strncpy(lp->d_typename, tp, sizeof (lp->d_typename));
1278 			continue;
1279 		}
1280 		if (!strcmp(cp, "label")) {
1281 			strncpy(lp->d_packname, tp, sizeof (lp->d_packname));
1282 			continue;
1283 		}
1284 		if (!strcmp(cp, "bytes/sector")) {
1285 			v = GETNUM(lp->d_secsize, tp, 1, &errstr);
1286 			if (errstr || (v % 512) != 0) {
1287 				warnx("line %d: bad %s: %s", lineno, cp, tp);
1288 				errors++;
1289 			} else
1290 				lp->d_secsize = v;
1291 			continue;
1292 		}
1293 		if (!strcmp(cp, "sectors/track")) {
1294 			v = GETNUM(lp->d_nsectors, tp, 1, &errstr);
1295 			if (errstr) {
1296 				warnx("line %d: bad %s: %s", lineno, cp, tp);
1297 				errors++;
1298 			} else
1299 				lp->d_nsectors = v;
1300 			continue;
1301 		}
1302 		if (!strcmp(cp, "sectors/cylinder")) {
1303 			v = GETNUM(lp->d_secpercyl, tp, 1, &errstr);
1304 			if (errstr) {
1305 				warnx("line %d: bad %s: %s", lineno, cp, tp);
1306 				errors++;
1307 			} else
1308 				lp->d_secpercyl = v;
1309 			continue;
1310 		}
1311 		if (!strcmp(cp, "tracks/cylinder")) {
1312 			v = GETNUM(lp->d_ntracks, tp, 1, &errstr);
1313 			if (errstr) {
1314 				warnx("line %d: bad %s: %s", lineno, cp, tp);
1315 				errors++;
1316 			} else
1317 				lp->d_ntracks = v;
1318 			continue;
1319 		}
1320 		if (!strcmp(cp, "cylinders")) {
1321 			v = GETNUM(lp->d_ncylinders, tp, 1, &errstr);
1322 			if (errstr) {
1323 				warnx("line %d: bad %s: %s", lineno, cp, tp);
1324 				errors++;
1325 			} else
1326 				lp->d_ncylinders = v;
1327 			continue;
1328 		}
1329 		if (!strcmp(cp, "total sectors")) {
1330 			lv = GETNUM(lv, tp, 1, &errstr);
1331 			if (errstr) {
1332 				warnx("line %d: bad %s: %s", lineno, cp, tp);
1333 				errors++;
1334 			} else {
1335 				DL_SETDSIZE(lp, lv);
1336 			}
1337 			continue;
1338 		}
1339 		if (!strcmp(cp, "rpm")) {
1340 			v = GETNUM(lp->d_rpm, tp, 1, &errstr);
1341 			if (errstr) {
1342 				warnx("line %d: bad %s: %s", lineno, cp, tp);
1343 				errors++;
1344 			} else
1345 				lp->d_rpm = v;
1346 			continue;
1347 		}
1348 		if (!strcmp(cp, "interleave")) {
1349 			v = GETNUM(lp->d_interleave, tp, 1, &errstr);
1350 			if (errstr) {
1351 				warnx("line %d: bad %s: %s", lineno, cp, tp);
1352 				errors++;
1353 			} else
1354 				lp->d_interleave = v;
1355 			continue;
1356 		}
1357 		if (!strcmp(cp, "trackskew")) {
1358 			v = GETNUM(lp->d_trackskew, tp, 0, &errstr);
1359 			if (errstr) {
1360 				warnx("line %d: bad %s: %s", lineno, cp, tp);
1361 				errors++;
1362 			} else
1363 				lp->d_trackskew = v;
1364 			continue;
1365 		}
1366 		if (!strcmp(cp, "cylinderskew")) {
1367 			v = GETNUM(lp->d_cylskew, tp, 0, &errstr);
1368 			if (errstr) {
1369 				warnx("line %d: bad %s: %s", lineno, cp, tp);
1370 				errors++;
1371 			} else
1372 				lp->d_cylskew = v;
1373 			continue;
1374 		}
1375 		if (!strcmp(cp, "headswitch")) {
1376 			v = GETNUM(lp->d_headswitch, tp, 0, &errstr);
1377 			if (errstr) {
1378 				warnx("line %d: bad %s: %s", lineno, cp, tp);
1379 				errors++;
1380 			} else
1381 				lp->d_headswitch = v;
1382 			continue;
1383 		}
1384 		if (!strcmp(cp, "track-to-track seek")) {
1385 			v = GETNUM(lp->d_trkseek, tp, 0, &errstr);
1386 			if (errstr) {
1387 				warnx("line %d: bad %s: %s", lineno, cp, tp);
1388 				errors++;
1389 			} else
1390 				lp->d_trkseek = v;
1391 			continue;
1392 		}
1393 		if ('a' <= *cp && *cp <= 'z' && cp[1] == '\0') {
1394 			unsigned int part = *cp - 'a';
1395 
1396 			if (part >= lp->d_npartitions) {
1397 				if (part >= MAXPARTITIONS) {
1398 					warnx("line %d: bad partition name: %s",
1399 					    lineno, cp);
1400 					errors++;
1401 					continue;
1402 				} else {
1403 					lp->d_npartitions = part + 1;
1404 				}
1405 			}
1406 			pp = &lp->d_partitions[part];
1407 #define NXTNUM(n, field, errstr) { \
1408 	if (tp == NULL) {					\
1409 		warnx("line %d: too few fields", lineno);	\
1410 		errors++;					\
1411 		break;						\
1412 	} else							\
1413 		cp = tp, tp = word(cp), (n) = GETNUM(field, cp, 0, errstr); \
1414 }
1415 			NXTNUM(lv, lv, &errstr);
1416 			if (errstr) {
1417 				warnx("line %d: bad partition size: %s",
1418 				    lineno, cp);
1419 				errors++;
1420 			} else {
1421 				DL_SETPSIZE(pp, lv);
1422 			}
1423 			NXTNUM(lv, lv, &errstr);
1424 			if (errstr) {
1425 				warnx("line %d: bad partition offset: %s",
1426 				    lineno, cp);
1427 				errors++;
1428 			} else {
1429 				DL_SETPOFFSET(pp, lv);
1430 			}
1431 			if (tp == NULL) {
1432 				pp->p_fstype = FS_UNUSED;
1433 				goto gottype;
1434 			}
1435 			cp = tp, tp = word(cp);
1436 			cpp = fstypenames;
1437 			for (; cpp < &fstypenames[FSMAXTYPES]; cpp++)
1438 				if ((s = *cpp) && !strcasecmp(s, cp)) {
1439 					pp->p_fstype = cpp - fstypenames;
1440 					goto gottype;
1441 				}
1442 			if (isdigit(*cp))
1443 				v = GETNUM(pp->p_fstype, cp, 0, &errstr);
1444 			else
1445 				v = FSMAXTYPES;
1446 			if (errstr || v >= FSMAXTYPES) {
1447 				warnx("line %d: warning, unknown filesystem type: %s",
1448 				    lineno, cp);
1449 				v = FS_UNUSED;
1450 			}
1451 			pp->p_fstype = v;
1452 	gottype:
1453 			switch (pp->p_fstype) {
1454 
1455 			case FS_UNUSED:				/* XXX */
1456 				if (tp == NULL)	/* ok to skip fsize/bsize */
1457 					break;
1458 				NXTNUM(fsize, fsize, &errstr);
1459 				if (fsize == 0)
1460 					break;
1461 				NXTNUM(v, v, &errstr);
1462 				pp->p_fragblock =
1463 				    DISKLABELV1_FFS_FRAGBLOCK(fsize, v / fsize);
1464 				break;
1465 
1466 			case FS_BSDFFS:
1467 				NXTNUM(fsize, fsize, &errstr);
1468 				if (fsize == 0)
1469 					break;
1470 				NXTNUM(v, v, &errstr);
1471 				pp->p_fragblock =
1472 				    DISKLABELV1_FFS_FRAGBLOCK(fsize, v / fsize);
1473 				NXTNUM(pp->p_cpg, pp->p_cpg, &errstr);
1474 				break;
1475 
1476 			default:
1477 				break;
1478 			}
1479 			continue;
1480 		}
1481 		warnx("line %d: unknown field: %s", lineno, cp);
1482 		errors++;
1483 	next:
1484 		;
1485 	}
1486 	errors += checklabel(lp);
1487 	return (errors == 0);
1488 }
1489 
1490 /*
1491  * Check disklabel for errors and fill in
1492  * derived fields according to supplied values.
1493  */
1494 int
1495 checklabel(struct disklabel *lp)
1496 {
1497 	struct partition *pp;
1498 	int i, errors = 0;
1499 	char part;
1500 
1501 	if (lp->d_secsize == 0) {
1502 		warnx("sector size %d", lp->d_secsize);
1503 		return (1);
1504 	}
1505 	if (lp->d_nsectors == 0) {
1506 		warnx("sectors/track %d", lp->d_nsectors);
1507 		return (1);
1508 	}
1509 	if (lp->d_ntracks == 0) {
1510 		warnx("tracks/cylinder %d", lp->d_ntracks);
1511 		return (1);
1512 	}
1513 	if  (lp->d_ncylinders == 0) {
1514 		warnx("cylinders/unit %d", lp->d_ncylinders);
1515 		errors++;
1516 	}
1517 	if (lp->d_rpm == 0)
1518 		warnx("warning, revolutions/minute %d", lp->d_rpm);
1519 	if (lp->d_secpercyl == 0)
1520 		lp->d_secpercyl = lp->d_nsectors * lp->d_ntracks;
1521 	if (DL_GETDSIZE(lp) == 0)
1522 		DL_SETDSIZE(lp, (u_int64_t)lp->d_secpercyl * lp->d_ncylinders);
1523 #ifdef i386__notyet
1524 	if (dosdp && dosdp->dp_size &&
1525 	    (dosdp->dp_typ == DOSPTYP_OPENBSD)) {
1526 		&& DL_GETDSIZE(lp) > dosdp->dp_start + dosdp->dp_size) {
1527 		warnx("exceeds DOS partition size");
1528 		errors++;
1529 		DL_SETDSIZE(lp, dosdp->dp_start + dosdp->dp_size);
1530 	}
1531 #endif
1532 	if (lp->d_bbsize == 0) {
1533 		warnx("boot block size %d", lp->d_bbsize);
1534 		errors++;
1535 	} else if (lp->d_bbsize % lp->d_secsize)
1536 		warnx("warning, boot block size %% sector-size != 0");
1537 	if (lp->d_sbsize == 0) {
1538 		warnx("super block size %d", lp->d_sbsize);
1539 		errors++;
1540 	} else if (lp->d_sbsize % lp->d_secsize)
1541 		warnx("warning, super block size %% sector-size != 0");
1542 	if (lp->d_npartitions > MAXPARTITIONS)
1543 		warnx("warning, number of partitions (%d) > MAXPARTITIONS (%d)",
1544 		    lp->d_npartitions, MAXPARTITIONS);
1545 	for (i = 0; i < lp->d_npartitions; i++) {
1546 		part = 'a' + i;
1547 		pp = &lp->d_partitions[i];
1548 		if (DL_GETPSIZE(pp) == 0 && DL_GETPOFFSET(pp) != 0)
1549 			warnx("warning, partition %c: size 0, but offset %lld",
1550 			    part, DL_GETPOFFSET(pp));
1551 #ifdef SUN_CYLCHECK
1552 		if (lp->d_flags & D_VENDOR) {
1553 			if (i != RAW_PART && DL_GETPSIZE(pp) % lp->d_secpercyl)
1554 				warnx("warning, partition %c: size %% "
1555 				    "cylinder-size != 0", part);
1556 			if (i != RAW_PART && DL_GETPOFFSET(pp) % lp->d_secpercyl)
1557 				warnx("warning, partition %c: offset %% "
1558 				    "cylinder-size != 0", part);
1559 		}
1560 #endif
1561 #ifdef SUN_AAT0
1562 		if ((lp->d_flags & D_VENDOR) &&
1563 		    i == 0 && DL_GETPSIZE(pp) != 0 && DL_GETPOFFSET(pp) != 0) {
1564 			warnx("this architecture requires partition 'a' to "
1565 			    "start at sector 0");
1566 			errors++;
1567 		}
1568 #endif
1569 		if (DL_GETPOFFSET(pp) > DL_GETDSIZE(lp)) {
1570 			warnx("partition %c: offset past end of unit", part);
1571 			errors++;
1572 		}
1573 		if (DL_GETPOFFSET(pp) + DL_GETPSIZE(pp) > DL_GETDSIZE(lp)) {
1574 			warnx("partition %c: partition extends past end of unit",
1575 			    part);
1576 			errors++;
1577 		}
1578 #if 0
1579 		if (pp->p_frag == 0 && pp->p_fsize != 0) {
1580 			warnx("partition %c: block size < fragment size", part);
1581 			errors++;
1582 		}
1583 #endif
1584 	}
1585 	for (; i < MAXPARTITIONS; i++) {
1586 		part = 'a' + i;
1587 		pp = &lp->d_partitions[i];
1588 		if (DL_GETPSIZE(pp) || DL_GETPOFFSET(pp))
1589 			warnx("warning, unused partition %c: size %lld offset %lld",
1590 			    'a' + i, DL_GETPSIZE(pp), DL_GETPOFFSET(pp));
1591 	}
1592 	return (errors);
1593 }
1594 
1595 #if NUMBOOT > 0
1596 /*
1597  * If we are installing a boot program that doesn't fit in d_bbsize
1598  * we need to mark those partitions that the boot overflows into.
1599  * This allows newfs to prevent creation of a filesystem where it might
1600  * clobber bootstrap code.
1601  */
1602 void
1603 setbootflag(struct disklabel *lp)
1604 {
1605 	struct partition *pp;
1606 	int i, errors = 0;
1607 	daddr64_t bend;
1608 	char part;
1609 
1610 	if (bootbuf == NULL)
1611 		return;
1612 
1613 	bend = bootsize / lp->d_secsize;
1614 	for (i = 0; i < lp->d_npartitions; i++) {
1615 		if (i == RAW_PART)
1616 			/* It will *ALWAYS* overlap 'c'. */
1617 			continue;
1618 		pp = &lp->d_partitions[i];
1619 		if (DL_GETPSIZE(pp) == 0)
1620 			/* Partition is unused. */
1621 			continue;
1622 		if (bend <= DL_GETPOFFSET(pp)) {
1623 			/* Boot blocks end before this partition starts. */
1624 			if (pp->p_fstype == FS_BOOT)
1625 				pp->p_fstype = FS_UNUSED;
1626 			continue;
1627 		}
1628 
1629 		part = 'a' + i;
1630 		switch (pp->p_fstype) {
1631 		case FS_BOOT:	/* Already marked. */
1632 			break;
1633 		case FS_UNUSED:	/* Mark. */
1634 			pp->p_fstype = FS_BOOT;
1635 			warnx("warning, boot overlaps partition %c, %s",
1636 			    part, "marked as FS_BOOT");
1637 			break;
1638 		default:
1639 			warnx("boot overlaps used partition %c", part);
1640 			errors++;
1641 			break;
1642 		}
1643 	}
1644 	if (errors)
1645 		errx(4, "cannot install boot program");
1646 }
1647 #endif
1648 
1649 int
1650 cmplabel(struct disklabel *lp1, struct disklabel *lp2)
1651 {
1652 	struct disklabel lab1 = *lp1;
1653 	struct disklabel lab2 = *lp2;
1654 
1655 	/* We don't compare these fields */
1656 	lab1.d_magic = lab2.d_magic;
1657 	lab1.d_magic2 = lab2.d_magic2;
1658 	lab1.d_checksum = lab2.d_checksum;
1659 	lab1.d_bbsize = lab2.d_bbsize;
1660 	lab1.d_sbsize = lab2.d_sbsize;
1661 
1662 	return (memcmp(&lab1, &lab2, sizeof(struct disklabel)));
1663 }
1664 
1665 void
1666 usage(void)
1667 {
1668 	char *boot = "";
1669 	char *blank = "       ";
1670 	char *Bflag = "";
1671 
1672 #if NUMBOOT == 1
1673 	Bflag = "B";
1674 	boot = " [-b boot1]";
1675 #endif
1676 
1677 	fprintf(stderr,
1678 	    "usage: disklabel [-c | -d | -t] [-v] [-p unit] disk\t\t(read)\n");
1679 	fprintf(stderr,
1680 	    "       disklabel -w [-c | -d] [-nv] disk disktype [packid]\t(write)\n");
1681 	fprintf(stderr,
1682 	    "       disklabel -e [-c | -d] [-nv] disk\t\t\t(edit)\n");
1683 	fprintf(stderr,
1684 	    "       disklabel -E [-c | -d] [-Anv] [-f tempfile] disk\t\t(simple editor)\n");
1685 	fprintf(stderr,
1686 	    "       disklabel -R [-nv] disk protofile\t\t\t(restore)\n");
1687 	fprintf(stderr,
1688 	    "       disklabel -N | -W [-nv] disk\t\t\t\t(protect)\n\n");
1689 #if NUMBOOT > 0
1690 	fprintf(stderr,
1691 	    "%sdisklabel -B  [-nv]%s disk [disktype]           (boot)\n",
1692 	    blank, boot);
1693 #endif
1694 	fprintf(stderr,
1695 	    "%sdisklabel -%sw [-nv]%s disk disktype [packid]    (write)\n",
1696 	    blank, Bflag, boot);
1697 	fprintf(stderr,
1698 	    "%sdisklabel -%sR [-nv]%s disk protofile [disktype] (restore)\n\n",
1699 	    blank, Bflag, boot);
1700 	fprintf(stderr,
1701 	    "`disk' may be of the form: sd0 or /dev/rsd0%c.\n", 'a'+RAW_PART);
1702 	fprintf(stderr,
1703 	    "`disktype' is an entry from %s, see disktab(5) for more info.\n",
1704 	    DISKTAB);
1705 	fprintf(stderr,
1706 	    "`packid' is an identification string for the device.\n");
1707 	fprintf(stderr,
1708 	    "`protofile' is the output from the read cmd form; -R is powerful.\n");
1709 #ifdef SEEALSO
1710 	fprintf(stderr,
1711 	    "For procedures specific to this architecture see: %s\n", SEEALSO);
1712 #endif
1713 	exit(1);
1714 }
1715