xref: /dragonfly/sbin/disklabel64/disklabel64.c (revision a32bc35d)
1 /*
2  * Copyright (c) 2007 The DragonFly Project.  All rights reserved.
3  *
4  * This code is derived from software contributed to The DragonFly Project
5  * by Matthew Dillon <dillon@backplane.com>
6  *
7  * Redistribution and use in source and binary forms, with or without
8  * modification, are permitted provided that the following conditions
9  * are met:
10  *
11  * 1. Redistributions of source code must retain the above copyright
12  *    notice, this list of conditions and the following disclaimer.
13  * 2. Redistributions in binary form must reproduce the above copyright
14  *    notice, this list of conditions and the following disclaimer in
15  *    the documentation and/or other materials provided with the
16  *    distribution.
17  * 3. Neither the name of The DragonFly Project nor the names of its
18  *    contributors may be used to endorse or promote products derived
19  *    from this software without specific, prior written permission.
20  *
21  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
22  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
23  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
24  * FOR A PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE
25  * COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
26  * INCIDENTAL, SPECIAL, EXEMPLARY OR CONSEQUENTIAL DAMAGES (INCLUDING,
27  * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
28  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
29  * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
30  * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
31  * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
32  * SUCH DAMAGE.
33  */
34 /*
35  * Copyright (c) 1987, 1993
36  *	The Regents of the University of California.  All rights reserved.
37  *
38  * This code is derived from software contributed to Berkeley by
39  * Symmetric Computer Systems.
40  *
41  * Redistribution and use in source and binary forms, with or without
42  * modification, are permitted provided that the following conditions
43  * are met:
44  * 1. Redistributions of source code must retain the above copyright
45  *    notice, this list of conditions and the following disclaimer.
46  * 2. Redistributions in binary form must reproduce the above copyright
47  *    notice, this list of conditions and the following disclaimer in the
48  *    documentation and/or other materials provided with the distribution.
49  * 3. All advertising materials mentioning features or use of this software
50  *    must display the following acknowledgement:
51  *	This product includes software developed by the University of
52  *	California, Berkeley and its contributors.
53  * 4. Neither the name of the University nor the names of its contributors
54  *    may be used to endorse or promote products derived from this software
55  *    without specific prior written permission.
56  *
57  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
58  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
59  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
60  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
61  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
62  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
63  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
64  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
65  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
66  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
67  * SUCH DAMAGE.
68  *
69  * @(#)disklabel.c	1.2 (Symmetric) 11/28/85
70  * @(#)disklabel.c      8.2 (Berkeley) 1/7/94
71  * $FreeBSD: src/sbin/disklabel/disklabel.c,v 1.28.2.15 2003/01/24 16:18:16 des Exp $
72  */
73 
74 #include <sys/param.h>
75 #include <sys/file.h>
76 #include <sys/stat.h>
77 #include <sys/wait.h>
78 #define DKTYPENAMES
79 #include <sys/disklabel64.h>
80 #include <sys/diskslice.h>
81 #include <sys/diskmbr.h>
82 #include <sys/dtype.h>
83 #include <sys/sysctl.h>
84 #include <disktab.h>
85 #include <fstab.h>
86 
87 #include <vfs/ufs/dinode.h>
88 #include <vfs/ufs/fs.h>
89 
90 #include <unistd.h>
91 #include <string.h>
92 #include <stdio.h>
93 #include <stdlib.h>
94 #include <signal.h>
95 #include <stdarg.h>
96 #include <stddef.h>
97 #include <ctype.h>
98 #include <err.h>
99 #include <errno.h>
100 #include <uuid.h>
101 #include "pathnames.h"
102 
103 extern uint32_t crc32(const void *buf, size_t size);
104 
105 /*
106  * Disklabel64: read and write 64 bit disklabels.
107  * The label is usually placed on one of the first sectors of the disk.
108  * Many machines also place a bootstrap in the same area,
109  * in which case the label is embedded in the bootstrap.
110  * The bootstrap source must leave space at the proper offset
111  * for the label on such machines.
112  */
113 
114 #define LABELSIZE	((sizeof(struct disklabel64) + 4095) & ~4095)
115 #define BOOTSIZE	32768
116 
117 /* FIX!  These are too low, but are traditional */
118 #define DEFAULT_NEWFS_BLOCK  8192U
119 #define DEFAULT_NEWFS_FRAG   1024U
120 #define DEFAULT_NEWFS_CPG    16U
121 
122 #define BIG_NEWFS_BLOCK  16384U
123 #define BIG_NEWFS_FRAG   2048U
124 #define BIG_NEWFS_CPG    64U
125 
126 void	makelabel(const char *, const char *, struct disklabel64 *);
127 int	writelabel(int, struct disklabel64 *);
128 void	l_perror(const char *);
129 struct disklabel64 *readlabel(int);
130 struct disklabel64 *makebootarea(int);
131 void	display(FILE *, const struct disklabel64 *);
132 int	edit(struct disklabel64 *, int);
133 int	editit(void);
134 char	*skip(char *);
135 char	*word(char *);
136 int	getasciilabel(FILE *, struct disklabel64 *);
137 int	getasciipartspec(char *, struct disklabel64 *, int, int, uint32_t);
138 int	getasciipartuuid(char *, struct disklabel64 *, int, int, uint32_t);
139 int	checklabel(struct disklabel64 *);
140 void	Warning(const char *, ...) __printflike(1, 2);
141 void	usage(void);
142 struct disklabel64 *getvirginlabel(void);
143 
144 #define	DEFEDITOR	_PATH_VI
145 #define	streq(a,b)	(strcmp(a,b) == 0)
146 
147 char	*dkname;
148 char	*specname;
149 char	tmpfil[] = PATH_TMPFILE;
150 
151 struct	disklabel64 lab;
152 
153 #define MAX_PART ('z')
154 #define MAX_NUM_PARTS (1 + MAX_PART - 'a')
155 char    part_size_type[MAX_NUM_PARTS];
156 char    part_offset_type[MAX_NUM_PARTS];
157 int     part_set[MAX_NUM_PARTS];
158 
159 int	installboot;	/* non-zero if we should install a boot program */
160 int	boot1size;
161 int	boot1lsize;
162 int	boot2size;
163 char	*boot1buf;
164 char	*boot2buf;
165 char	*boot1path;
166 char	*boot2path;
167 
168 enum	{
169 	UNSPEC, EDIT, NOWRITE, READ, RESTORE, WRITE, WRITEABLE, WRITEBOOT
170 } op = UNSPEC;
171 
172 int	rflag;
173 int	Vflag;
174 int	disable_write;   /* set to disable writing to disk label */
175 u_int32_t slice_start_lba;
176 
177 #ifdef DEBUG
178 int	debug;
179 #define OPTIONS	"BNRWb:denrs:Vw"
180 #else
181 #define OPTIONS	"BNRWb:enrs:Vw"
182 #endif
183 
184 int
185 main(int argc, char *argv[])
186 {
187 	struct disklabel64 *lp;
188 	FILE *t;
189 	int ch, f = 0, flag, error = 0;
190 	char *name = NULL;
191 
192 	while ((ch = getopt(argc, argv, OPTIONS)) != -1)
193 		switch (ch) {
194 			case 'B':
195 				++installboot;
196 				break;
197 			case 'b':
198 				boot1path = optarg;
199 				break;
200 
201 			case 's':
202 				boot2path = optarg;
203 				break;
204 			case 'N':
205 				if (op != UNSPEC)
206 					usage();
207 				op = NOWRITE;
208 				break;
209 			case 'n':
210 				disable_write = 1;
211 				break;
212 			case 'R':
213 				if (op != UNSPEC)
214 					usage();
215 				op = RESTORE;
216 				break;
217 			case 'W':
218 				if (op != UNSPEC)
219 					usage();
220 				op = WRITEABLE;
221 				break;
222 			case 'e':
223 				if (op != UNSPEC)
224 					usage();
225 				op = EDIT;
226 				break;
227 			case 'V':
228 				++Vflag;
229 				break;
230 			case 'r':
231 				++rflag;
232 				break;
233 			case 'w':
234 				if (op != UNSPEC)
235 					usage();
236 				op = WRITE;
237 				break;
238 #ifdef DEBUG
239 			case 'd':
240 				debug++;
241 				break;
242 #endif
243 			case '?':
244 			default:
245 				usage();
246 		}
247 	argc -= optind;
248 	argv += optind;
249 	if (installboot) {
250 		rflag++;
251 		if (op == UNSPEC)
252 			op = WRITEBOOT;
253 	} else {
254 		if (op == UNSPEC)
255 			op = READ;
256 		boot1path = NULL;
257 		boot2path = NULL;
258 	}
259 	if (argc < 1)
260 		usage();
261 
262 	dkname = getdevpath(argv[0], 0);
263 	specname = dkname;
264 	f = open(specname, op == READ ? O_RDONLY : O_RDWR);
265 	if (f < 0)
266 		err(4, "%s", specname);
267 
268 	switch(op) {
269 
270 	case UNSPEC:
271 		break;
272 
273 	case EDIT:
274 		if (argc != 1)
275 			usage();
276 		lp = readlabel(f);
277 		error = edit(lp, f);
278 		break;
279 
280 	case NOWRITE:
281 		flag = 0;
282 		if (ioctl(f, DIOCWLABEL, (char *)&flag) < 0)
283 			err(4, "ioctl DIOCWLABEL");
284 		break;
285 
286 	case READ:
287 		if (argc != 1)
288 			usage();
289 		lp = readlabel(f);
290 		display(stdout, lp);
291 		error = checklabel(lp);
292 		break;
293 
294 	case RESTORE:
295 		if (installboot && argc == 3) {
296 			makelabel(argv[2], 0, &lab);
297 			argc--;
298 
299 			/*
300 			 * We only called makelabel() for its side effect
301 			 * of setting the bootstrap file names.  Discard
302 			 * all changes to `lab' so that all values in the
303 			 * final label come from the ASCII label.
304 			 */
305 			bzero((char *)&lab, sizeof(lab));
306 		}
307 		if (argc != 2)
308 			usage();
309 		if (!(t = fopen(argv[1], "r")))
310 			err(4, "%s", argv[1]);
311 		if (!getasciilabel(t, &lab))
312 			exit(1);
313 		lp = makebootarea(f);
314 		bcopy(&lab.d_magic, &lp->d_magic,
315 		      sizeof(lab) - offsetof(struct disklabel64, d_magic));
316 		error = writelabel(f, lp);
317 		break;
318 
319 	case WRITE:
320 		if (argc == 3) {
321 			name = argv[2];
322 			argc--;
323 		}
324 		if (argc != 2)
325 			usage();
326 		makelabel(argv[1], name, &lab);
327 		lp = makebootarea(f);
328 		bcopy(&lab.d_magic, &lp->d_magic,
329 		      sizeof(lab) - offsetof(struct disklabel64, d_magic));
330 		if (checklabel(lp) == 0)
331 			error = writelabel(f, lp);
332 		break;
333 
334 	case WRITEABLE:
335 		flag = 1;
336 		if (ioctl(f, DIOCWLABEL, (char *)&flag) < 0)
337 			err(4, "ioctl DIOCWLABEL");
338 		break;
339 
340 	case WRITEBOOT:
341 	{
342 		struct disklabel64 tlab;
343 
344 		lp = readlabel(f);
345 		tlab = *lp;
346 		if (argc == 2)
347 			makelabel(argv[1], 0, &lab);
348 		lp = makebootarea(f);
349 		bcopy(&tlab.d_magic, &lp->d_magic,
350 		      sizeof(tlab) - offsetof(struct disklabel64, d_magic));
351 		if (checklabel(lp) == 0)
352 			error = writelabel(f, lp);
353 		break;
354 	}
355 	}
356 	exit(error);
357 }
358 
359 /*
360  * Construct a prototype disklabel from /etc/disktab.  As a side
361  * effect, set the names of the primary and secondary boot files
362  * if specified.
363  */
364 void
365 makelabel(const char *type, const char *name, struct disklabel64 *lp)
366 {
367 	struct disklabel64 *dp;
368 
369 	if (strcmp(type, "auto") == 0)
370 		dp = getvirginlabel();
371 	else
372 		dp = NULL;
373 	if (dp == NULL)
374 		errx(1, "%s: unknown disk type", type);
375 	*lp = *dp;
376 
377 	/*
378 	 * NOTE: boot control files may no longer be specified in disktab.
379 	 */
380 	if (name)
381 		strncpy(lp->d_packname, name, sizeof(lp->d_packname));
382 }
383 
384 int
385 writelabel(int f, struct disklabel64 *lp)
386 {
387 	struct disklabel64 *blp;
388 	int flag;
389 	int r;
390 	size_t lpsize;
391 	size_t lpcrcsize;
392 
393 	lpsize = offsetof(struct disklabel64, d_partitions[lp->d_npartitions]);
394 	lpcrcsize = lpsize - offsetof(struct disklabel64, d_magic);
395 
396 	if (disable_write) {
397 		Warning("write to disk label suppressed - label was as follows:");
398 		display(stdout, lp);
399 		return (0);
400 	} else {
401 		lp->d_magic = DISKMAGIC64;
402 		lp->d_crc = 0;
403 		lp->d_crc = crc32(&lp->d_magic, lpcrcsize);
404 		if (rflag) {
405 			/*
406 			 * Make sure the boot area is not too large
407 			 */
408 			if (boot2buf) {
409 				int lpbsize = (int)(lp->d_pbase - lp->d_bbase);
410 				if (lp->d_pbase == 0) {
411 					errx(1, "no space was set aside in "
412 						"the disklabel for boot2!");
413 				}
414 				if (boot2size > lpbsize) {
415 					errx(1, "label did not reserve enough "
416 						"space for boot!  %d/%d",
417 					     boot2size, lpbsize);
418 				}
419 			}
420 
421 			/*
422 			 * First set the kernel disk label,
423 			 * then write a label to the raw disk.
424 			 * If the SDINFO ioctl fails because it is
425 			 * unimplemented, keep going; otherwise, the kernel
426 			 * consistency checks may prevent us from changing
427 			 * the current (in-core) label.
428 			 */
429 			if (ioctl(f, DIOCSDINFO64, lp) < 0 &&
430 				errno != ENODEV && errno != ENOTTY) {
431 				l_perror("ioctl DIOCSDINFO");
432 				return (1);
433 			}
434 			lseek(f, (off_t)0, SEEK_SET);
435 
436 			/*
437 			 * The disklabel embeds areas which we may not
438 			 * have wanted to change.  Merge those areas in
439 			 * from disk.
440 			 */
441 			blp = makebootarea(f);
442 			if (blp != lp) {
443 				bcopy(&lp->d_magic, &blp->d_magic,
444 				      sizeof(*lp) -
445 				      offsetof(struct disklabel64, d_magic));
446 			}
447 
448 			/*
449 			 * write enable label sector before write
450 			 * (if necessary), disable after writing.
451 			 */
452 			flag = 1;
453 			if (ioctl(f, DIOCWLABEL, &flag) < 0)
454 				warn("ioctl DIOCWLABEL");
455 
456 			r = write(f, boot1buf, boot1lsize);
457 			if (r != (ssize_t)boot1lsize) {
458 				warn("write");
459 				return (1);
460 			}
461 			/*
462 			 * Output the remainder of the disklabel
463 			 */
464 			if (boot2buf) {
465 				lseek(f, lp->d_bbase, 0);
466 				r = write(f, boot2buf, boot2size);
467 				if (r != boot2size) {
468 					warn("write");
469 					return(1);
470 				}
471 			}
472 			flag = 0;
473 			ioctl(f, DIOCWLABEL, &flag);
474 		} else if (ioctl(f, DIOCWDINFO64, lp) < 0) {
475 			l_perror("ioctl DIOCWDINFO64");
476 			return (1);
477 		}
478 	}
479 	return (0);
480 }
481 
482 void
483 l_perror(const char *s)
484 {
485 	switch (errno) {
486 
487 	case ESRCH:
488 		warnx("%s: no disk label on disk;", s);
489 		fprintf(stderr, "add \"-r\" to install initial label\n");
490 		break;
491 
492 	case EINVAL:
493 		warnx("%s: label magic number or checksum is wrong!", s);
494 		fprintf(stderr, "(disklabel or kernel is out of date?)\n");
495 		break;
496 
497 	case EBUSY:
498 		warnx("%s: open partition would move or shrink", s);
499 		break;
500 
501 	case ENOATTR:
502 		warnx("%s: the disk already has a label of a different type,\n"
503 		      "probably a 32 bit disklabel.  It must be cleaned out "
504 		      "first.\n", s);
505 		break;
506 
507 	default:
508 		warn(NULL);
509 		break;
510 	}
511 }
512 
513 /*
514  * Fetch disklabel for disk.
515  * Use ioctl to get label unless -r flag is given.
516  */
517 struct disklabel64 *
518 readlabel(int f)
519 {
520 	struct disklabel64 *lp;
521 	u_int32_t savecrc;
522 	size_t lpcrcsize;
523 
524 	if (rflag) {
525 		/*
526 		 * Allocate space for the label.  The boot1 code, if any,
527 		 * is embedded in the label.  The label overlaps the boot1
528 		 * code.
529 		 */
530 		lp = makebootarea(f);
531 		lpcrcsize = offsetof(struct disklabel64,
532 				     d_partitions[lp->d_npartitions]) -
533 			    offsetof(struct disklabel64, d_magic);
534 		savecrc = lp->d_crc;
535 		lp->d_crc = 0;
536 		if (lp->d_magic != DISKMAGIC64)
537 			errx(1, "bad pack magic number");
538 		if (lp->d_npartitions > MAXPARTITIONS64 ||
539 		    savecrc != crc32(&lp->d_magic, lpcrcsize)
540 		) {
541 			errx(1, "corrupted disklabel64");
542 		}
543 		lp->d_crc = savecrc;
544 	} else {
545 		/*
546 		 * Just use a static structure to hold the label.  Note
547 		 * that DIOCSDINFO64 does not overwrite the boot1 area
548 		 * even though it is part of the disklabel64 structure.
549 		 */
550 		lp = &lab;
551 		if (Vflag) {
552 			if (ioctl(f, DIOCGDVIRGIN64, lp) < 0) {
553 				l_perror("ioctl DIOCGDVIRGIN64");
554 				exit(4);
555 			}
556 		} else {
557 			if (ioctl(f, DIOCGDINFO64, lp) < 0) {
558 				l_perror("ioctl DIOCGDINFO64");
559 				exit(4);
560 			}
561 		}
562 	}
563 	return (lp);
564 }
565 
566 /*
567  * Construct a boot area for boot1 and boot2 and return the location of
568  * the label within the area.  The caller will overwrite the label so
569  * we don't actually have to read it.
570  */
571 struct disklabel64 *
572 makebootarea(int f)
573 {
574 	struct disklabel64 *lp;
575 	struct partinfo info;
576 	u_int32_t secsize;
577 	struct stat st;
578 	int fd;
579 	int r;
580 
581 	if (ioctl(f, DIOCGPART, &info) == 0)
582 		secsize = info.media_blksize;
583 	else
584 		secsize = 512;
585 
586 	if (boot1buf == NULL) {
587 		size_t rsize;
588 
589 		rsize = (sizeof(struct disklabel64) + secsize - 1) &
590 			~(secsize - 1);
591 		boot1size = offsetof(struct disklabel64, d_magic);
592 		boot1lsize = rsize;
593 		boot1buf = malloc(rsize);
594 		bzero(boot1buf, rsize);
595 		r = read(f, boot1buf, rsize);
596 		if (r != (int)rsize)
597 			err(4, "%s", specname);
598 	}
599 	lp = (void *)boot1buf;
600 
601 	if (installboot == 0)
602 		return(lp);
603 
604 	if (boot2buf == NULL) {
605 		boot2size = 32768;
606 		boot2buf = malloc(boot2size);
607 		bzero(boot2buf, boot2size);
608 	}
609 
610 	/*
611 	 * If installing the boot code, read it into the appropriate portions
612 	 * of the buffer(s)
613 	 */
614 	if (boot1path == NULL)
615 		asprintf(&boot1path, "%s/boot1_64", _PATH_BOOTDIR);
616 	if (boot2path == NULL)
617 		asprintf(&boot2path, "%s/boot2_64", _PATH_BOOTDIR);
618 
619 	if ((fd = open(boot1path, O_RDONLY)) < 0)
620 		err(4, "%s", boot1path);
621 	if (fstat(fd, &st) < 0)
622 		err(4, "%s", boot1path);
623 	if (st.st_size > boot1size)
624 		err(4, "%s must be exactly %d bytes!", boot1path, boot1size);
625 	if (read(fd, boot1buf, boot1size) != boot1size)
626 		err(4, "%s must be exactly %d bytes!", boot1path, boot1size);
627 	close(fd);
628 
629 	if ((fd = open(boot2path, O_RDONLY)) < 0)
630 		err(4, "%s", boot2path);
631 	if (fstat(fd, &st) < 0)
632 		err(4, "%s", boot2path);
633 	if (st.st_size > boot2size)
634 		err(4, "%s must be <= %d bytes!", boot2path, boot2size);
635 	if ((r = read(fd, boot2buf, boot2size)) < 1)
636 		err(4, "%s is empty!", boot2path);
637 	boot2size = (r + secsize - 1) & ~(secsize - 1);
638 	close(fd);
639 
640 	/*
641 	 * XXX dangerously dedicated support goes here XXX
642 	 */
643 	return (lp);
644 }
645 
646 void
647 display(FILE *f, const struct disklabel64 *lp)
648 {
649 	const struct partition64 *pp;
650 	char *str;
651 	unsigned int part;
652 	int didany;
653 	uint32_t blksize;
654 
655 	/*
656 	 * Use a human readable block size if possible.  This is for
657 	 * display and editing purposes only.
658 	 */
659 	if (lp->d_align > 1024)
660 		blksize = 1024;
661 	else
662 		blksize = lp->d_align;
663 
664 	fprintf(f, "# %s:\n", specname);
665 	fprintf(f, "#\n");
666 	fprintf(f, "# Informational fields calculated from the above\n");
667 	fprintf(f, "# All byte equivalent offsets must be aligned\n");
668 	fprintf(f, "#\n");
669 	fprintf(f, "# boot space: %10ju bytes\n",
670 		(intmax_t)(lp->d_pbase - lp->d_bbase));
671 	fprintf(f, "# data space: %10ju blocks\t# %6.2f MB (%ju bytes)\n",
672 			(intmax_t)(lp->d_pstop - lp->d_pbase) / blksize,
673 			(double)(lp->d_pstop - lp->d_pbase) / 1024.0 / 1024.0,
674 			(intmax_t)(lp->d_pstop - lp->d_pbase));
675 	fprintf(f, "#\n");
676 	fprintf(f, "# NOTE: If the partition data base looks odd it may be\n");
677 	fprintf(f, "#       physically aligned instead of slice-aligned\n");
678 	fprintf(f, "#\n");
679 
680 	uuid_to_string(&lp->d_stor_uuid, &str, NULL);
681 	fprintf(f, "diskid: %s\n", str ? str : "<unknown>");
682 	free(str);
683 
684 	fprintf(f, "label: %.*s\n", (int)sizeof(lp->d_packname),
685 		lp->d_packname);
686 	fprintf(f, "boot2 data base:      0x%012jx\n", (intmax_t)lp->d_bbase);
687 	fprintf(f, "partitions data base: 0x%012jx\n", (intmax_t)lp->d_pbase);
688 	fprintf(f, "partitions data stop: 0x%012jx\n", (intmax_t)lp->d_pstop);
689 	fprintf(f, "backup label:         0x%012jx\n", (intmax_t)lp->d_abase);
690 	fprintf(f, "total size:           0x%012jx\t# %6.2f MB\n",
691 		(intmax_t)lp->d_total_size,
692 		(double)lp->d_total_size / 1024.0 / 1024.0);
693 	fprintf(f, "alignment: %u\n", lp->d_align);
694 	fprintf(f, "display block size: %u\t# for partition display only\n",
695 		blksize);
696 
697 	fprintf(f, "\n");
698 	fprintf(f, "%u partitions:\n", lp->d_npartitions);
699 	fprintf(f, "#          size     offset    fstype   fsuuid\n");
700 	didany = 0;
701 	for (part = 0; part < lp->d_npartitions; part++) {
702 		pp = &lp->d_partitions[part];
703 		const u_long onemeg = 1024 * 1024;
704 
705 		if (pp->p_bsize == 0)
706 			continue;
707 		didany = 1;
708 		fprintf(f, "  %c: ", 'a' + part);
709 
710 		if (pp->p_bsize % lp->d_align)
711 		    fprintf(f, "%10s  ", "ILLEGAL");
712 		else
713 		    fprintf(f, "%10ju ", (intmax_t)pp->p_bsize / blksize);
714 
715 		if ((pp->p_boffset - lp->d_pbase) % lp->d_align)
716 		    fprintf(f, "%10s  ", "ILLEGAL");
717 		else
718 		    fprintf(f, "%10ju  ",
719 			    (intmax_t)(pp->p_boffset - lp->d_pbase) / blksize);
720 
721 		if (pp->p_fstype < FSMAXTYPES)
722 			fprintf(f, "%8.8s", fstypenames[pp->p_fstype]);
723 		else
724 			fprintf(f, "%8d", pp->p_fstype);
725 		fprintf(f, "\t# %11.3fMB", (double)pp->p_bsize / onemeg);
726 		fprintf(f, "\n");
727 	}
728 	for (part = 0; part < lp->d_npartitions; part++) {
729 		pp = &lp->d_partitions[part];
730 
731 		if (pp->p_bsize == 0)
732 			continue;
733 
734 		if (uuid_is_nil(&lp->d_stor_uuid, NULL) == 0) {
735 			fprintf(f, "  %c-stor_uuid: ", 'a' + part);
736 			str = NULL;
737 			uuid_to_string(&pp->p_stor_uuid, &str, NULL);
738 			if (str) {
739 				fprintf(f, "%s", str);
740 				free(str);
741 			}
742 			fprintf(f, "\n");
743 		}
744 	}
745 	if (didany == 0) {
746 		fprintf(f, "# EXAMPLE\n");
747 		fprintf(f, "#a:          4g          0    4.2BSD\n");
748 		fprintf(f, "#a:          *           *    4.2BSD\n");
749 
750 	}
751 	fflush(f);
752 }
753 
754 int
755 edit(struct disklabel64 *lp, int f)
756 {
757 	int c, fd;
758 	struct disklabel64 label;
759 	FILE *fp;
760 
761 	if ((fd = mkstemp(tmpfil)) == -1 ||
762 	    (fp = fdopen(fd, "w")) == NULL) {
763 		warnx("can't create %s", tmpfil);
764 		return (1);
765 	}
766 	display(fp, lp);
767 	fclose(fp);
768 	for (;;) {
769 		if (!editit())
770 			break;
771 		fp = fopen(tmpfil, "r");
772 		if (fp == NULL) {
773 			warnx("can't reopen %s for reading", tmpfil);
774 			break;
775 		}
776 		bzero((char *)&label, sizeof(label));
777 		if (getasciilabel(fp, &label)) {
778 			*lp = label;
779 			if (writelabel(f, lp) == 0) {
780 				fclose(fp);
781 				unlink(tmpfil);
782 				return (0);
783 			}
784 		}
785 		fclose(fp);
786 		printf("re-edit the label? [y]: "); fflush(stdout);
787 		c = getchar();
788 		if (c != EOF && c != (int)'\n')
789 			while (getchar() != (int)'\n')
790 				;
791 		if  (c == (int)'n')
792 			break;
793 	}
794 	unlink(tmpfil);
795 	return (1);
796 }
797 
798 int
799 editit(void)
800 {
801 	int pid, xpid;
802 	int status, omask;
803 	const char *ed;
804 
805 	omask = sigblock(sigmask(SIGINT)|sigmask(SIGQUIT)|sigmask(SIGHUP));
806 	while ((pid = fork()) < 0) {
807 		if (errno == EPROCLIM) {
808 			warnx("you have too many processes");
809 			return(0);
810 		}
811 		if (errno != EAGAIN) {
812 			warn("fork");
813 			return(0);
814 		}
815 		sleep(1);
816 	}
817 	if (pid == 0) {
818 		sigsetmask(omask);
819 		setgid(getgid());
820 		setuid(getuid());
821 		if ((ed = getenv("EDITOR")) == NULL)
822 			ed = DEFEDITOR;
823 		execlp(ed, ed, tmpfil, NULL);
824 		err(1, "%s", ed);
825 	}
826 	while ((xpid = wait(&status)) >= 0)
827 		if (xpid == pid)
828 			break;
829 	sigsetmask(omask);
830 	return(!status);
831 }
832 
833 char *
834 skip(char *cp)
835 {
836 
837 	while (*cp != '\0' && isspace(*cp))
838 		cp++;
839 	if (*cp == '\0' || *cp == '#')
840 		return (NULL);
841 	return (cp);
842 }
843 
844 char *
845 word(char *cp)
846 {
847 	char c;
848 
849 	while (*cp != '\0' && !isspace(*cp) && *cp != '#')
850 		cp++;
851 	if ((c = *cp) != '\0') {
852 		*cp++ = '\0';
853 		if (c != '#')
854 			return (skip(cp));
855 	}
856 	return (NULL);
857 }
858 
859 /*
860  * Read an ascii label in from fd f,
861  * in the same format as that put out by display(),
862  * and fill in lp.
863  */
864 int
865 getasciilabel(FILE *f, struct disklabel64 *lp)
866 {
867 	char *cp;
868 	u_int part;
869 	char *tp, line[BUFSIZ];
870 	u_long v;
871 	uint32_t blksize = 0;
872 	uint64_t vv;
873 	int lineno = 0, errors = 0;
874 	char empty[] = "";
875 
876 	bzero(&part_set, sizeof(part_set));
877 	bzero(&part_size_type, sizeof(part_size_type));
878 	bzero(&part_offset_type, sizeof(part_offset_type));
879 	while (fgets(line, sizeof(line) - 1, f)) {
880 		lineno++;
881 		if ((cp = strchr(line,'\n')) != NULL)
882 			*cp = '\0';
883 		cp = skip(line);
884 		if (cp == NULL)
885 			continue;
886 		tp = strchr(cp, ':');
887 		if (tp == NULL) {
888 			fprintf(stderr, "line %d: syntax error\n", lineno);
889 			errors++;
890 			continue;
891 		}
892 		*tp++ = '\0', tp = skip(tp);
893 		if (sscanf(cp, "%lu partitions", &v) == 1) {
894 			if (v == 0 || v > MAXPARTITIONS64) {
895 				fprintf(stderr,
896 				    "line %d: bad # of partitions\n", lineno);
897 				lp->d_npartitions = MAXPARTITIONS64;
898 				errors++;
899 			} else
900 				lp->d_npartitions = v;
901 			continue;
902 		}
903 		if (tp == NULL)
904 			tp = empty;
905 
906 		if (streq(cp, "diskid")) {
907 			uint32_t status = 0;
908 			uuid_from_string(tp, &lp->d_stor_uuid, &status);
909 			if (status != uuid_s_ok) {
910 				fprintf(stderr,
911 				    "line %d: %s: illegal UUID\n",
912 				    lineno, tp);
913 				errors++;
914 			}
915 			continue;
916 		}
917 		if (streq(cp, "label")) {
918 			strncpy(lp->d_packname, tp, sizeof (lp->d_packname));
919 			continue;
920 		}
921 
922 		if (streq(cp, "alignment")) {
923 			v = strtoul(tp, NULL, 0);
924 			if (v <= 0 || (v & DEV_BMASK) != 0 || v > 1024*1024) {
925 				fprintf(stderr,
926 				    "line %d: %s: bad alignment\n",
927 				    lineno, tp);
928 				errors++;
929 			} else {
930 				lp->d_align = v;
931 			}
932 			continue;
933 		}
934 		if (streq(cp, "total size")) {
935 			vv = strtoull(tp, NULL, 0);
936 			if (vv == 0 || vv == (uint64_t)-1) {
937 				fprintf(stderr, "line %d: %s: bad %s\n",
938 				    lineno, tp, cp);
939 				errors++;
940 			} else {
941 				lp->d_total_size = vv;
942 			}
943 			continue;
944 		}
945 		if (streq(cp, "boot2 data base")) {
946 			vv = strtoull(tp, NULL, 0);
947 			if (vv == 0 || vv == (uint64_t)-1) {
948 				fprintf(stderr, "line %d: %s: bad %s\n",
949 				    lineno, tp, cp);
950 				errors++;
951 			} else {
952 				lp->d_bbase = vv;
953 			}
954 			continue;
955 		}
956 		if (streq(cp, "partitions data base")) {
957 			vv = strtoull(tp, NULL, 0);
958 			if (vv == 0 || vv == (uint64_t)-1) {
959 				fprintf(stderr, "line %d: %s: bad %s\n",
960 				    lineno, tp, cp);
961 				errors++;
962 			} else {
963 				lp->d_pbase = vv;
964 			}
965 			continue;
966 		}
967 		if (streq(cp, "partitions data stop")) {
968 			vv = strtoull(tp, NULL, 0);
969 			if (vv == 0 || vv == (uint64_t)-1) {
970 				fprintf(stderr, "line %d: %s: bad %s\n",
971 				    lineno, tp, cp);
972 				errors++;
973 			} else {
974 				lp->d_pstop = vv;
975 			}
976 			continue;
977 		}
978 		if (streq(cp, "backup label")) {
979 			vv = strtoull(tp, NULL, 0);
980 			if (vv == 0 || vv == (uint64_t)-1) {
981 				fprintf(stderr, "line %d: %s: bad %s\n",
982 				    lineno, tp, cp);
983 				errors++;
984 			} else {
985 				lp->d_abase = vv;
986 			}
987 			continue;
988 		}
989 		if (streq(cp, "display block size")) {
990 			v = strtoul(tp, NULL, 0);
991 			if (v <= 0 || (v & DEV_BMASK) != 0 || v > 1024*1024) {
992 				fprintf(stderr,
993 				    "line %d: %s: bad alignment\n",
994 				    lineno, tp);
995 				errors++;
996 			} else {
997 				blksize = v;
998 			}
999 			continue;
1000 		}
1001 
1002 		/* the ':' was removed above */
1003 
1004 		/*
1005 		 * Handle main partition data, e.g. a:, b:, etc.
1006 		 */
1007 		if (*cp < 'a' || *cp > MAX_PART) {
1008 			fprintf(stderr,
1009 			    "line %d: %s: Unknown disklabel field\n", lineno,
1010 			    cp);
1011 			errors++;
1012 			continue;
1013 		}
1014 
1015 		/* Process a partition specification line. */
1016 		part = *cp - 'a';
1017 		if (part >= lp->d_npartitions) {
1018 			fprintf(stderr,
1019 			    "line %d: partition name out of range a-%c: %s\n",
1020 			    lineno, 'a' + lp->d_npartitions - 1, cp);
1021 			errors++;
1022 			continue;
1023 		}
1024 
1025 		if (blksize == 0) {
1026 			fprintf(stderr, "block size to use for partition "
1027 					"display was not specified!\n");
1028 			errors++;
1029 			continue;
1030 		}
1031 
1032 		if (strcmp(cp + 1, "-stor_uuid") == 0) {
1033 			if (getasciipartuuid(tp, lp, part, lineno, blksize)) {
1034 				errors++;
1035 				break;
1036 			}
1037 			continue;
1038 		} else if (cp[1] == 0) {
1039 			part_set[part] = 1;
1040 			if (getasciipartspec(tp, lp, part, lineno, blksize)) {
1041 				errors++;
1042 				break;
1043 			}
1044 			continue;
1045 		}
1046 		fprintf(stderr, "line %d: %s: Unknown disklabel field\n",
1047 			lineno, cp);
1048 		errors++;
1049 		continue;
1050 	}
1051 	errors += checklabel(lp);
1052 	return (errors == 0);
1053 }
1054 
1055 static
1056 int
1057 parse_field_val(char **tp, char **cp, u_int64_t *vv, int lineno)
1058 {
1059 	char *tmp;
1060 
1061 	if (*tp == NULL || **tp == 0) {
1062 		fprintf(stderr, "line %d: too few numeric fields\n", lineno);
1063 		return(-1);
1064 	}
1065 	*cp = *tp;
1066 	*tp = word(*cp);
1067 	*vv = strtoull(*cp, &tmp, 0);
1068 	if (*vv == ULLONG_MAX) {
1069 		fprintf(stderr, "line %d: illegal number\n", lineno);
1070 		return(-1);
1071 	}
1072 	if (tmp)
1073 		return(*tmp);
1074 	else
1075 		return(0);
1076 }
1077 
1078 /*
1079  * Read a partition line into partition `part' in the specified disklabel.
1080  * Return 0 on success, 1 on failure.
1081  */
1082 int
1083 getasciipartspec(char *tp, struct disklabel64 *lp, int part,
1084 		 int lineno, uint32_t blksize)
1085 {
1086 	struct partition64 *pp;
1087 	char *cp;
1088 	const char **cpp;
1089 	int r;
1090 	u_long v;
1091 	uint64_t vv;
1092 	uint64_t mpx;
1093 
1094 	pp = &lp->d_partitions[part];
1095 	cp = NULL;
1096 
1097 	/*
1098 	 * size
1099 	 */
1100 	r = parse_field_val(&tp, &cp, &vv, lineno);
1101 	if (r < 0)
1102 		return (1);
1103 
1104 	mpx = 1;
1105 	switch(r) {
1106 	case 0:
1107 		mpx = blksize;
1108 		break;
1109 	case '%':
1110 		/* mpx = 1; */
1111 		break;
1112 	case '*':
1113 		mpx = 0;
1114 		break;
1115 	case 'g':
1116 	case 'G':
1117 		mpx *= 1024ULL;
1118 		/* fall through */
1119 	case 'm':
1120 	case 'M':
1121 		mpx *= 1024ULL;
1122 		/* fall through */
1123 	case 'k':
1124 	case 'K':
1125 		mpx *= 1024ULL;
1126 		r = 0;			/* eat the suffix */
1127 		break;
1128 	default:
1129 		Warning("unknown size specifier '%c' (*/%%/K/M/G are valid)",
1130 			r);
1131 		return(1);
1132 	}
1133 
1134 	part_size_type[part] = r;
1135 	if (vv == 0 && r != '*') {
1136 		fprintf(stderr,
1137 		    "line %d: %s: bad partition size (0)\n", lineno, cp);
1138 		return (1);
1139 	}
1140 	pp->p_bsize = vv * mpx;
1141 
1142 	/*
1143 	 * offset
1144 	 */
1145 	r = parse_field_val(&tp, &cp, &vv, lineno);
1146 	if (r < 0)
1147 		return (1);
1148 	part_offset_type[part] = r;
1149 	switch(r) {
1150 	case '*':
1151 		pp->p_boffset = 0;
1152 		break;
1153 	case 0:
1154 		pp->p_boffset = vv * blksize + lp->d_pbase;
1155 		break;
1156 	default:
1157 		fprintf(stderr,
1158 		    "line %d: %s: bad suffix on partition offset (%c)\n",
1159 		    lineno, cp, r);
1160 		return (1);
1161 	}
1162 
1163 	/*
1164 	 * fstype
1165 	 */
1166 	cp = tp;
1167 	tp = word(cp);
1168 	for (cpp = fstypenames; cpp < &fstypenames[FSMAXTYPES]; cpp++) {
1169 		if (*cpp && strcasecmp(*cpp, cp) == 0)
1170 			break;
1171 	}
1172 	if (*cpp != NULL) {
1173 		pp->p_fstype = cpp - fstypenames;
1174 	} else {
1175 		if (isdigit(*cp))
1176 			v = strtoul(cp, NULL, 0);
1177 		else
1178 			v = FSMAXTYPES;
1179 		if (v >= FSMAXTYPES) {
1180 			fprintf(stderr,
1181 			    "line %d: Warning, unknown filesystem type %s\n",
1182 			    lineno, cp);
1183 			v = FS_UNUSED;
1184 		}
1185 		pp->p_fstype = v;
1186 	}
1187 
1188 	cp = tp;
1189 	if (tp) {
1190 		fprintf(stderr, "line %d: Warning, extra data on line\n",
1191 			lineno);
1192 	}
1193 	return(0);
1194 }
1195 
1196 int
1197 getasciipartuuid(char *tp, struct disklabel64 *lp, int part,
1198 		 int lineno, uint32_t blksize __unused)
1199 {
1200 	struct partition64 *pp;
1201 	uint32_t status;
1202 	char *cp;
1203 
1204 	pp = &lp->d_partitions[part];
1205 
1206 	cp = tp;
1207 	tp = word(cp);
1208 	uuid_from_string(cp, &pp->p_stor_uuid, &status);
1209 	if (status != uuid_s_ok) {
1210 		fprintf(stderr, "line %d: Illegal storage uuid specification\n",
1211 			lineno);
1212 		return(1);
1213 	}
1214 	return(0);
1215 }
1216 
1217 /*
1218  * Check disklabel for errors and fill in
1219  * derived fields according to supplied values.
1220  */
1221 int
1222 checklabel(struct disklabel64 *lp)
1223 {
1224 	struct partition64 *pp;
1225 	int errors = 0;
1226 	char part;
1227 	u_int64_t total_size;
1228 	u_int64_t current_offset;
1229 	u_long total_percent;
1230 	int seen_default_offset;
1231 	int hog_part;
1232 	int i, j;
1233 	struct partition64 *pp2;
1234 	u_int64_t off;
1235 
1236 	if (lp->d_align < 512 ||
1237 	    (lp->d_align ^ (lp->d_align - 1)) != lp->d_align * 2 - 1) {
1238 		Warning("Illegal alignment specified: %u\n", lp->d_align);
1239 		return (1);
1240 	}
1241 	if (lp->d_npartitions > MAXPARTITIONS64) {
1242 		Warning("number of partitions (%u) > MAXPARTITIONS (%d)",
1243 			lp->d_npartitions, MAXPARTITIONS64);
1244 		return (1);
1245 	}
1246 	off = offsetof(struct disklabel64, d_partitions[lp->d_npartitions]);
1247 	off = (off + lp->d_align - 1) & ~(int64_t)(lp->d_align - 1);
1248 
1249 	if (lp->d_bbase < off || lp->d_bbase % lp->d_align) {
1250 		Warning("illegal boot2 data base ");
1251 		return (1);
1252 	}
1253 
1254 	/*
1255 	 * pbase can be unaligned slice-relative but will be
1256 	 * aligned physically.
1257 	 */
1258 	if (lp->d_pbase < lp->d_bbase) {
1259 		Warning("illegal partition data base");
1260 		return (1);
1261 	}
1262 	if (lp->d_pstop < lp->d_pbase) {
1263 		Warning("illegal partition data stop");
1264 		return (1);
1265 	}
1266 	if (lp->d_pstop > lp->d_total_size) {
1267 		printf("%012jx\n%012jx\n",
1268 			(intmax_t)lp->d_pstop, (intmax_t)lp->d_total_size);
1269 		Warning("disklabel control info is beyond the total size");
1270 		return (1);
1271 	}
1272 	if (lp->d_abase &&
1273 	    (lp->d_abase < lp->d_pstop ||
1274 	     lp->d_abase > lp->d_total_size - off)) {
1275 		Warning("illegal backup label location");
1276 		return (1);
1277 	}
1278 
1279 	/* first allocate space to the partitions, then offsets */
1280 	total_size = 0;		/* in bytes */
1281 	total_percent = 0;	/* in percent */
1282 	hog_part = -1;
1283 	/* find all fixed partitions */
1284 	for (i = 0; i < (int)lp->d_npartitions; i++) {
1285 		pp = &lp->d_partitions[i];
1286 		if (part_set[i]) {
1287 			if (part_size_type[i] == '*') {
1288 				if (part_offset_type[i] != '*') {
1289 					if (total_size < pp->p_boffset)
1290 						total_size = pp->p_boffset;
1291 				}
1292 				if (hog_part != -1) {
1293 					Warning("Too many '*' partitions (%c and %c)",
1294 					    hog_part + 'a',i + 'a');
1295 				} else {
1296 					hog_part = i;
1297 				}
1298 			} else {
1299 				off_t size;
1300 
1301 				size = pp->p_bsize;
1302 				if (part_size_type[i] == '%') {
1303 					/*
1304 					 * don't count %'s yet
1305 					 */
1306 					total_percent += size;
1307 				} else {
1308 					/*
1309 					 * Value has already been converted
1310 					 * to bytes.
1311 					 */
1312 					if (size % lp->d_align != 0) {
1313 						Warning("partition %c's size is not properly aligned",
1314 							i + 'a');
1315 					}
1316 					total_size += size;
1317 				}
1318 			}
1319 		}
1320 	}
1321 	/* handle % partitions - note %'s don't need to add up to 100! */
1322 	if (total_percent != 0) {
1323 		int64_t free_space;
1324 		int64_t space_left;
1325 
1326 		free_space = (int64_t)(lp->d_pstop - lp->d_pbase - total_size);
1327 		free_space &= ~(u_int64_t)(lp->d_align - 1);
1328 
1329 		space_left = free_space;
1330 		if (total_percent > 100) {
1331 			fprintf(stderr,"total percentage %lu is greater than 100\n",
1332 			    total_percent);
1333 			errors++;
1334 		}
1335 
1336 		if (free_space > 0) {
1337 			for (i = 0; i < (int)lp->d_npartitions; i++) {
1338 				pp = &lp->d_partitions[i];
1339 				if (part_set[i] && part_size_type[i] == '%') {
1340 					/* careful of overflows! and integer roundoff */
1341 					pp->p_bsize = ((double)pp->p_bsize/100) * free_space;
1342 					pp->p_bsize = (pp->p_bsize + lp->d_align - 1) & ~(u_int64_t)(lp->d_align - 1);
1343 					if ((int64_t)pp->p_bsize > space_left)
1344 						pp->p_bsize = (u_int64_t)space_left;
1345 					total_size += pp->p_bsize;
1346 					space_left -= pp->p_bsize;
1347 				}
1348 			}
1349 		} else {
1350 			fprintf(stderr, "%jd bytes available to give to "
1351 					"'*' and '%%' partitions\n",
1352 				(intmax_t)free_space);
1353 			errors++;
1354 			/* fix?  set all % partitions to size 0? */
1355 		}
1356 	}
1357 	/* give anything remaining to the hog partition */
1358 	if (hog_part != -1) {
1359 		off = lp->d_pstop - lp->d_pbase - total_size;
1360 		off &= ~(u_int64_t)(lp->d_align - 1);
1361 		lp->d_partitions[hog_part].p_bsize = off;
1362 		total_size = lp->d_pstop - lp->d_pbase;
1363 	}
1364 
1365 	/* Now set the offsets for each partition */
1366 	current_offset = lp->d_pbase;
1367 	seen_default_offset = 0;
1368 	for (i = 0; i < (int)lp->d_npartitions; i++) {
1369 		part = 'a' + i;
1370 		pp = &lp->d_partitions[i];
1371 		if (pp->p_bsize == 0)
1372 			continue;
1373 		if (part_set[i]) {
1374 			if (part_offset_type[i] == '*') {
1375 				pp->p_boffset = current_offset;
1376 				seen_default_offset = 1;
1377 			} else {
1378 				/* allow them to be out of order for old-style tables */
1379 				if (pp->p_boffset < current_offset &&
1380 				    seen_default_offset &&
1381 				    pp->p_fstype != FS_VINUM) {
1382 					fprintf(stderr,
1383 "Offset 0x%012jx for partition %c overlaps previous partition which ends at 0x%012jx\n",
1384 					    (intmax_t)pp->p_boffset,
1385 					    i + 'a',
1386 					    (intmax_t)current_offset);
1387 					fprintf(stderr,
1388 "Labels with any *'s for offset must be in ascending order by sector\n");
1389 					errors++;
1390 				} else if (pp->p_boffset != current_offset &&
1391 					   seen_default_offset) {
1392 					/*
1393 					 * this may give unneeded warnings if
1394 					 * partitions are out-of-order
1395 					 */
1396 					Warning(
1397 "Offset 0x%012jx for partition %c doesn't match expected value 0x%012jx",
1398 					    pp->p_boffset, i + 'a',
1399 					    (intmax_t)current_offset);
1400 				}
1401 			}
1402 			current_offset = pp->p_boffset + pp->p_bsize;
1403 		}
1404 	}
1405 
1406 	for (i = 0; i < (int)lp->d_npartitions; i++) {
1407 		part = 'a' + i;
1408 		pp = &lp->d_partitions[i];
1409 		if (pp->p_bsize == 0 && pp->p_boffset != 0)
1410 			Warning("partition %c: size 0, but offset 0x%012jx",
1411 				part, (intmax_t)pp->p_boffset);
1412 		if (pp->p_bsize == 0) {
1413 			pp->p_boffset = 0;
1414 			continue;
1415 		}
1416 		if (uuid_is_nil(&pp->p_stor_uuid, NULL))
1417 			uuid_create(&pp->p_stor_uuid, NULL);
1418 
1419 		if (pp->p_boffset < lp->d_pbase) {
1420 			fprintf(stderr,
1421 			    "partition %c: offset out of bounds (%jd)\n",
1422 			    part, (intmax_t)(pp->p_boffset - lp->d_pbase));
1423 			errors++;
1424 		}
1425 		if (pp->p_boffset > lp->d_pstop) {
1426 			fprintf(stderr,
1427 			    "partition %c: offset out of bounds (%jd)\n",
1428 			    part, (intmax_t)(pp->p_boffset - lp->d_pbase));
1429 			errors++;
1430 		}
1431 		if (pp->p_boffset + pp->p_bsize > lp->d_pstop) {
1432 			fprintf(stderr,
1433 			    "partition %c: size out of bounds (%jd)\n",
1434 			    part, (intmax_t)(pp->p_boffset - lp->d_pbase));
1435 			errors++;
1436 		}
1437 
1438 		/* check for overlaps */
1439 		/* this will check for all possible overlaps once and only once */
1440 		for (j = 0; j < i; j++) {
1441 			pp2 = &lp->d_partitions[j];
1442 			if (pp->p_fstype != FS_VINUM &&
1443 			    pp2->p_fstype != FS_VINUM &&
1444 			    part_set[i] && part_set[j]) {
1445 				if (pp2->p_boffset < pp->p_boffset + pp->p_bsize &&
1446 				    (pp2->p_boffset + pp2->p_bsize > pp->p_boffset ||
1447 					pp2->p_boffset >= pp->p_boffset)) {
1448 					fprintf(stderr,"partitions %c and %c overlap!\n",
1449 					    j + 'a', i + 'a');
1450 					errors++;
1451 				}
1452 			}
1453 		}
1454 	}
1455 	for (; i < (int)lp->d_npartitions; i++) {
1456 		part = 'a' + i;
1457 		pp = &lp->d_partitions[i];
1458 		if (pp->p_bsize || pp->p_boffset)
1459 			Warning("unused partition %c: size 0x%012jx "
1460 				"offset 0x%012jx",
1461 				'a' + i, (intmax_t)pp->p_bsize,
1462 				(intmax_t)pp->p_boffset);
1463 	}
1464 	return (errors);
1465 }
1466 
1467 /*
1468  * When operating on a "virgin" disk, try getting an initial label
1469  * from the associated device driver.  This might work for all device
1470  * drivers that are able to fetch some initial device parameters
1471  * without even having access to a (BSD) disklabel, like SCSI disks,
1472  * most IDE drives, or vn devices.
1473  *
1474  * The device name must be given in its "canonical" form.
1475  */
1476 static struct disklabel64 dlab;
1477 
1478 struct disklabel64 *
1479 getvirginlabel(void)
1480 {
1481 	struct disklabel64 *dl = &dlab;
1482 	int f;
1483 
1484 	if ((f = open(dkname, O_RDONLY)) == -1) {
1485 		warn("cannot open %s", dkname);
1486 		return (NULL);
1487 	}
1488 
1489 	/*
1490 	 * Try to use the new get-virgin-label ioctl.  If it fails,
1491 	 * fallback to the old get-disk-info ioctl.
1492 	 */
1493 	if (ioctl(f, DIOCGDVIRGIN64, dl) < 0) {
1494 		l_perror("ioctl DIOCGDVIRGIN64");
1495 		close(f);
1496 		return (NULL);
1497 	}
1498 	close(f);
1499 	return (dl);
1500 }
1501 
1502 /*VARARGS1*/
1503 void
1504 Warning(const char *fmt, ...)
1505 {
1506 	va_list ap;
1507 
1508 	fprintf(stderr, "Warning, ");
1509 	va_start(ap, fmt);
1510 	vfprintf(stderr, fmt, ap);
1511 	fprintf(stderr, "\n");
1512 	va_end(ap);
1513 }
1514 
1515 void
1516 usage(void)
1517 {
1518 	fprintf(stderr, "%s\n%s\n%s\n%s\n%s\n%s\n%s\n%s\n%s\n%s\n%s\n%s\n%s\n%s\n%s\n%s\n",
1519 		"usage: disklabel64 [-r] disk",
1520 		"\t\t(to read label)",
1521 		"       disklabel64 -w [-r] [-n] disk type [packid]",
1522 		"\t\t(to write label with existing boot program)",
1523 		"       disklabel64 -e [-r] [-n] disk",
1524 		"\t\t(to edit label)",
1525 		"       disklabel64 -R [-r] [-n] disk protofile",
1526 		"\t\t(to restore label with existing boot program)",
1527 		"       disklabel64 -B [-n] [-b boot1 -s boot2] disk [type]",
1528 		"\t\t(to install boot program with existing label)",
1529 		"       disklabel64 -w -B [-n] [-b boot1 -s boot2] disk type [packid]",
1530 		"\t\t(to write label and boot program)",
1531 		"       disklabel64 -R -B [-n] [-b boot1 -s boot2] disk protofile [type]",
1532 		"\t\t(to restore label and boot program)",
1533 		"       disklabel64 [-NW] disk",
1534 		"\t\t(to write disable/enable label)");
1535 	exit(1);
1536 }
1537