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