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