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