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