xref: /freebsd/sbin/bsdlabel/bsdlabel.c (revision 63a938566d524836885917d95bd491aa4400b181)
1 /*-
2  * SPDX-License-Identifier: BSD-4-Clause
3  *
4  * Copyright (c) 1994, 1995 Gordon W. Ross
5  * Copyright (c) 1994 Theo de Raadt
6  * All rights reserved.
7  * Copyright (c) 1987, 1993
8  *	The Regents of the University of California.  All rights reserved.
9  *
10  * This code is derived from software contributed to Berkeley by
11  * Symmetric Computer Systems.
12  *
13  * Redistribution and use in source and binary forms, with or without
14  * modification, are permitted provided that the following conditions
15  * are met:
16  * 1. Redistributions of source code must retain the above copyright
17  *    notice, this list of conditions and the following disclaimer.
18  * 2. Redistributions in binary form must reproduce the above copyright
19  *    notice, this list of conditions and the following disclaimer in the
20  *    documentation and/or other materials provided with the distribution.
21  * 3. All advertising materials mentioning features or use of this software
22  *    must display the following acknowledgement:
23  *	This product includes software developed by the University of
24  *	California, Berkeley and its contributors.
25  *      This product includes software developed by Theo de Raadt.
26  * 4. Neither the name of the University nor the names of its contributors
27  *    may be used to endorse or promote products derived from this software
28  *    without specific prior written permission.
29  *
30  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
31  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
32  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
33  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
34  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
35  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
36  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
37  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
38  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
39  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
40  * SUCH DAMAGE.
41  *
42  *	from: $NetBSD: disksubr.c,v 1.13 2000/12/17 22:39:18 pk $
43  */
44 
45 #if 0
46 #ifndef lint
47 static const char copyright[] =
48 "@(#) Copyright (c) 1987, 1993\n\
49 	The Regents of the University of California.  All rights reserved.\n";
50 #endif /* not lint */
51 
52 #ifndef lint
53 static char sccsid[] = "@(#)disklabel.c	8.2 (Berkeley) 1/7/94";
54 /* from static char sccsid[] = "@(#)disklabel.c	1.2 (Symmetric) 11/28/85"; */
55 #endif /* not lint */
56 #endif
57 #include <sys/cdefs.h>
58 __FBSDID("$FreeBSD$");
59 
60 #include <sys/param.h>
61 #include <stdint.h>
62 #include <sys/file.h>
63 #include <sys/stat.h>
64 #include <sys/wait.h>
65 #include <sys/disk.h>
66 #define DKTYPENAMES
67 #define FSTYPENAMES
68 #define MAXPARTITIONS	20
69 #include <sys/disklabel.h>
70 
71 #include <unistd.h>
72 #include <string.h>
73 #include <stdio.h>
74 #include <libgeom.h>
75 #include <stdlib.h>
76 #include <signal.h>
77 #include <stdarg.h>
78 #include <ctype.h>
79 #include <err.h>
80 #include <errno.h>
81 
82 #include "pathnames.h"
83 
84 static void	makelabel(const char *, struct disklabel *);
85 static int	geom_class_available(const char *);
86 static int	writelabel(void);
87 static int	readlabel(int flag);
88 static void	display(FILE *, const struct disklabel *);
89 static int	edit(void);
90 static int	editit(void);
91 static void	fixlabel(struct disklabel *);
92 static char	*skip(char *);
93 static char	*word(char *);
94 static int	getasciilabel(FILE *, struct disklabel *);
95 static int	getasciipartspec(char *, struct disklabel *, int, int);
96 static int	checklabel(struct disklabel *);
97 static void	usage(void);
98 static struct disklabel *getvirginlabel(void);
99 
100 #define	DEFEDITOR	_PATH_VI
101 #define	DEFPARTITIONS	8
102 
103 static char	*specname;
104 static char	*pname;
105 static char	tmpfil[] = PATH_TMPFILE;
106 
107 static struct	disklabel lab;
108 static u_char	bootarea[BBSIZE];
109 static off_t	mediasize;
110 static ssize_t	secsize;
111 static char	blank[] = "";
112 static char	unknown[] = "unknown";
113 
114 #define MAX_PART ('z')
115 #define MAX_NUM_PARTS (1 + MAX_PART - 'a')
116 static char    part_size_type[MAX_NUM_PARTS];
117 static char    part_offset_type[MAX_NUM_PARTS];
118 static int     part_set[MAX_NUM_PARTS];
119 
120 static int	installboot;	/* non-zero if we should install a boot program */
121 static int	allfields;	/* present all fields in edit */
122 static char const *xxboot;	/* primary boot */
123 
124 static uint32_t lba_offset;
125 #ifndef LABELSECTOR
126 #define LABELSECTOR -1
127 #endif
128 #ifndef LABELOFFSET
129 #define LABELOFFSET -1
130 #endif
131 static int labelsoffset = LABELSECTOR;
132 static int labeloffset = LABELOFFSET;
133 static int bbsize = BBSIZE;
134 
135 static enum {
136 	UNSPEC, EDIT, READ, RESTORE, WRITE, WRITEBOOT
137 } op = UNSPEC;
138 
139 
140 static int	disable_write;   /* set to disable writing to disk label */
141 static int	is_file;	/* work on a file (abs. pathname), "-f" opt. */
142 
143 int
144 main(int argc, char *argv[])
145 {
146 	FILE *t;
147 	int ch, error, fd;
148 	const char *name;
149 
150 	error = 0;
151 	name = NULL;
152 
153 	while ((ch = getopt(argc, argv, "ABb:efm:nRrw")) != -1)
154 		switch (ch) {
155 			case 'A':
156 				allfields = 1;
157 				break;
158 			case 'B':
159 				++installboot;
160 				break;
161 			case 'b':
162 				xxboot = optarg;
163 				break;
164 			case 'f':
165 				is_file=1;
166 				break;
167 			case 'm':
168 				if (!strcmp(optarg, "i386") ||
169 				    !strcmp(optarg, "amd64")) {
170 					labelsoffset = 1;
171 					labeloffset = 0;
172 					bbsize = 8192;
173 				} else {
174 					errx(1, "Unsupported architecture");
175 				}
176 				break;
177 			case 'n':
178 				disable_write = 1;
179 				break;
180 			case 'R':
181 				if (op != UNSPEC)
182 					usage();
183 				op = RESTORE;
184 				break;
185 			case 'e':
186 				if (op != UNSPEC)
187 					usage();
188 				op = EDIT;
189 				break;
190 			case 'r':
191 				/*
192 				 * We accept and ignore -r for compatibility with
193 				 * historical disklabel usage.
194 				 */
195 				break;
196 			case 'w':
197 				if (op != UNSPEC)
198 					usage();
199 				op = WRITE;
200 				break;
201 			case '?':
202 			default:
203 				usage();
204 		}
205 	argc -= optind;
206 	argv += optind;
207 
208 	if (argc < 1)
209 		usage();
210 	if (labelsoffset < 0 || labeloffset < 0)
211 		errx(1, "a -m <architecture> option must be specified");
212 
213 	/* Figure out the names of the thing we're working on */
214 	if (is_file) {
215 		specname = argv[0];
216 	} else {
217 		specname = g_device_path(argv[0]);
218 		if (specname == NULL) {
219 			warn("unable to get correct path for %s", argv[0]);
220 			return(1);
221 		}
222 		fd = open(specname, O_RDONLY);
223 		if (fd < 0) {
224 			warn("error opening %s", specname);
225 			return(1);
226 		}
227 		pname = g_providername(fd);
228 		if (pname == NULL) {
229 			warn("error getting providername for %s", specname);
230 			close(fd);
231 			return(1);
232 		}
233 		close(fd);
234 	}
235 
236 	if (installboot && op == UNSPEC)
237 		op = WRITEBOOT;
238 	else if (op == UNSPEC)
239 		op = READ;
240 
241 	switch(op) {
242 
243 	case UNSPEC:
244 		break;
245 
246 	case EDIT:
247 		if (argc != 1)
248 			usage();
249 		readlabel(1);
250 		fixlabel(&lab);
251 		error = edit();
252 		break;
253 
254 	case READ:
255 		if (argc != 1)
256 			usage();
257 		readlabel(1);
258 		display(stdout, NULL);
259 		error = checklabel(NULL);
260 		break;
261 
262 	case RESTORE:
263 		if (argc != 2)
264 			usage();
265 		if (!(t = fopen(argv[1], "r")))
266 			err(4, "fopen %s", argv[1]);
267 		readlabel(0);
268 		if (!getasciilabel(t, &lab))
269 			exit(1);
270 		error = writelabel();
271 		break;
272 
273 	case WRITE:
274 		if (argc == 2)
275 			name = argv[1];
276 		else if (argc == 1)
277 			name = "auto";
278 		else
279 			usage();
280 		readlabel(0);
281 		makelabel(name, &lab);
282 		fixlabel(&lab);
283 		if (checklabel(NULL) == 0)
284 			error = writelabel();
285 		break;
286 
287 	case WRITEBOOT:
288 
289 		readlabel(1);
290 		fixlabel(&lab);
291 		if (argc == 2)
292 			makelabel(argv[1], &lab);
293 		if (checklabel(NULL) == 0)
294 			error = writelabel();
295 		break;
296 	}
297 	exit(error);
298 }
299 
300 static void
301 fixlabel(struct disklabel *lp)
302 {
303 	struct partition *dp;
304 	int i;
305 
306 	for (i = 0; i < lp->d_npartitions; i++) {
307 		if (i == RAW_PART)
308 			continue;
309 		if (lp->d_partitions[i].p_size)
310 			return;
311 	}
312 
313 	dp = &lp->d_partitions[0];
314 	dp->p_offset = BBSIZE / secsize;
315 	dp->p_size = lp->d_secperunit - dp->p_offset;
316 }
317 
318 /*
319  * Construct a prototype disklabel from /etc/disktab.
320  */
321 static void
322 makelabel(const char *type, struct disklabel *lp)
323 {
324 	struct disklabel *dp;
325 
326 	if (strcmp(type, "auto") == 0)
327 		dp = getvirginlabel();
328 	else
329 		dp = getdiskbyname(type);
330 	if (dp == NULL)
331 		errx(1, "%s: unknown disk type", type);
332 	*lp = *dp;
333 	bzero(lp->d_packname, sizeof(lp->d_packname));
334 }
335 
336 static void
337 readboot(void)
338 {
339 	int fd;
340 	struct stat st;
341 
342 	if (xxboot == NULL)
343 		xxboot = "/boot/boot";
344 	fd = open(xxboot, O_RDONLY);
345 	if (fd < 0)
346 		err(1, "cannot open %s", xxboot);
347 	fstat(fd, &st);
348 	if (st.st_size <= BBSIZE) {
349 		if (read(fd, bootarea, st.st_size) != st.st_size)
350 			err(1, "read error %s", xxboot);
351 		close(fd);
352 		return;
353 	}
354 	errx(1, "boot code %s is wrong size", xxboot);
355 }
356 
357 static int
358 geom_class_available(const char *name)
359 {
360 	struct gclass *class;
361 	struct gmesh mesh;
362 	int error;
363 
364 	error = geom_gettree(&mesh);
365 	if (error != 0)
366 		errc(1, error, "Cannot get GEOM tree");
367 
368 	LIST_FOREACH(class, &mesh.lg_class, lg_class) {
369 		if (strcmp(class->lg_name, name) == 0) {
370 			geom_deletetree(&mesh);
371 			return (1);
372 		}
373 	}
374 
375 	geom_deletetree(&mesh);
376 
377 	return (0);
378 }
379 
380 static int
381 writelabel(void)
382 {
383 	int i, fd, serrno;
384 	struct gctl_req *grq;
385 	char const *errstr;
386 	struct disklabel *lp = &lab;
387 
388 	if (disable_write) {
389 		warnx("write to disk label suppressed - label was as follows:");
390 		display(stdout, NULL);
391 		return (0);
392 	}
393 
394 	lp->d_magic = DISKMAGIC;
395 	lp->d_magic2 = DISKMAGIC;
396 	lp->d_checksum = 0;
397 	lp->d_checksum = dkcksum(lp);
398 	if (installboot)
399 		readboot();
400 	for (i = 0; i < lab.d_npartitions; i++)
401 		if (lab.d_partitions[i].p_size)
402 			lab.d_partitions[i].p_offset += lba_offset;
403 	bsd_disklabel_le_enc(bootarea + labeloffset + labelsoffset * lab.d_secsize,
404 	    lp);
405 
406 	fd = open(specname, O_RDWR);
407 	if (fd < 0) {
408 		if (is_file) {
409 			warn("cannot open file %s for writing label", specname);
410 			return(1);
411 		} else
412 			serrno = errno;
413 
414 		if (geom_class_available("PART") != 0) {
415 			/*
416 			 * Since we weren't able open provider for
417 			 * writing, then recommend user to use gpart(8).
418 			 */
419 			warnc(serrno,
420 			    "cannot open provider %s for writing label",
421 			    specname);
422 			warnx("Try to use gpart(8).");
423 			return (1);
424 		}
425 
426 		/* Give up if GEOM_BSD is not available. */
427 		if (geom_class_available("BSD") == 0) {
428 			warnc(serrno, "%s", specname);
429 			return (1);
430 		}
431 
432 		grq = gctl_get_handle();
433 		gctl_ro_param(grq, "verb", -1, "write label");
434 		gctl_ro_param(grq, "class", -1, "BSD");
435 		gctl_ro_param(grq, "geom", -1, pname);
436 		gctl_ro_param(grq, "label", 148+16*8,
437 			bootarea + labeloffset + labelsoffset * lab.d_secsize);
438 		errstr = gctl_issue(grq);
439 		if (errstr != NULL) {
440 			warnx("%s", errstr);
441 			gctl_free(grq);
442 			return(1);
443 		}
444 		gctl_free(grq);
445 		if (installboot) {
446 			grq = gctl_get_handle();
447 			gctl_ro_param(grq, "verb", -1, "write bootcode");
448 			gctl_ro_param(grq, "class", -1, "BSD");
449 			gctl_ro_param(grq, "geom", -1, pname);
450 			gctl_ro_param(grq, "bootcode", BBSIZE, bootarea);
451 			errstr = gctl_issue(grq);
452 			if (errstr != NULL) {
453 				warnx("%s", errstr);
454 				gctl_free(grq);
455 				return (1);
456 			}
457 			gctl_free(grq);
458 		}
459 	} else {
460 		if (write(fd, bootarea, bbsize) != bbsize) {
461 			warn("write %s", specname);
462 			close (fd);
463 			return (1);
464 		}
465 		close (fd);
466 	}
467 	return (0);
468 }
469 
470 static void
471 get_file_parms(int f)
472 {
473 	int i;
474 	struct stat sb;
475 
476 	if (fstat(f, &sb) != 0)
477 		err(4, "fstat failed");
478 	i = sb.st_mode & S_IFMT;
479 	if (i != S_IFREG && i != S_IFLNK)
480 		errx(4, "%s is not a valid file or link", specname);
481 	secsize = DEV_BSIZE;
482 	mediasize = sb.st_size;
483 }
484 
485 /*
486  * Fetch disklabel for disk.
487  */
488 static int
489 readlabel(int flag)
490 {
491 	ssize_t nbytes;
492 	uint32_t lba;
493 	int f, i;
494 	int error;
495 
496 	f = open(specname, O_RDONLY);
497 	if (f < 0)
498 		err(1, "%s", specname);
499 	if (is_file)
500 		get_file_parms(f);
501 	else {
502 		mediasize = g_mediasize(f);
503 		secsize = g_sectorsize(f);
504 		if (secsize < 0 || mediasize < 0)
505 			err(4, "cannot get disk geometry");
506 	}
507 	if (mediasize > (off_t)0xffffffff * secsize)
508 		errx(1,
509 		    "disks with more than 2^32-1 sectors are not supported");
510 	(void)lseek(f, (off_t)0, SEEK_SET);
511 	nbytes = read(f, bootarea, BBSIZE);
512 	if (nbytes == -1)
513 		err(4, "%s read", specname);
514 	if (nbytes != BBSIZE)
515 		errx(4, "couldn't read %d bytes from %s", BBSIZE, specname);
516 	close (f);
517 	error = bsd_disklabel_le_dec(
518 	    bootarea + (labeloffset + labelsoffset * secsize),
519 	    &lab, MAXPARTITIONS);
520 	if (flag && error)
521 		errx(1, "%s: no valid label found", specname);
522 
523 	if (is_file)
524 		return(0);
525 
526 	/*
527 	 * Compensate for absolute block addressing by finding the
528 	 * smallest partition offset and if the offset of the 'c'
529 	 * partition is equal to that, subtract it from all offsets.
530 	 */
531 	lba = ~0;
532 	for (i = 0; i < lab.d_npartitions; i++) {
533 		if (lab.d_partitions[i].p_size)
534 			lba = MIN(lba, lab.d_partitions[i].p_offset);
535 	}
536 	if (lba != 0 && lab.d_partitions[RAW_PART].p_offset == lba) {
537 		for (i = 0; i < lab.d_npartitions; i++) {
538 			if (lab.d_partitions[i].p_size)
539 				lab.d_partitions[i].p_offset -= lba;
540 		}
541 		/*
542 		 * Save the offset so that we can write the label
543 		 * back with absolute block addresses.
544 		 */
545 		lba_offset = lba;
546 	}
547 	return (error);
548 }
549 
550 
551 static void
552 display(FILE *f, const struct disklabel *lp)
553 {
554 	int i, j;
555 	const struct partition *pp;
556 
557 	if (lp == NULL)
558 		lp = &lab;
559 
560 	fprintf(f, "# %s:\n", specname);
561 	if (allfields) {
562 		if (lp->d_type < DKMAXTYPES)
563 			fprintf(f, "type: %s\n", dktypenames[lp->d_type]);
564 		else
565 			fprintf(f, "type: %u\n", lp->d_type);
566 		fprintf(f, "disk: %.*s\n", (int)sizeof(lp->d_typename),
567 			lp->d_typename);
568 		fprintf(f, "label: %.*s\n", (int)sizeof(lp->d_packname),
569 			lp->d_packname);
570 		fprintf(f, "flags:");
571 		if (lp->d_flags & D_REMOVABLE)
572 			fprintf(f, " removeable");
573 		if (lp->d_flags & D_ECC)
574 			fprintf(f, " ecc");
575 		if (lp->d_flags & D_BADSECT)
576 			fprintf(f, " badsect");
577 		fprintf(f, "\n");
578 		fprintf(f, "bytes/sector: %lu\n", (u_long)lp->d_secsize);
579 		fprintf(f, "sectors/track: %lu\n", (u_long)lp->d_nsectors);
580 		fprintf(f, "tracks/cylinder: %lu\n", (u_long)lp->d_ntracks);
581 		fprintf(f, "sectors/cylinder: %lu\n", (u_long)lp->d_secpercyl);
582 		fprintf(f, "cylinders: %lu\n", (u_long)lp->d_ncylinders);
583 		fprintf(f, "sectors/unit: %lu\n", (u_long)lp->d_secperunit);
584 		fprintf(f, "rpm: %u\n", lp->d_rpm);
585 		fprintf(f, "interleave: %u\n", lp->d_interleave);
586 		fprintf(f, "trackskew: %u\n", lp->d_trackskew);
587 		fprintf(f, "cylinderskew: %u\n", lp->d_cylskew);
588 		fprintf(f, "headswitch: %lu\t\t# milliseconds\n",
589 		    (u_long)lp->d_headswitch);
590 		fprintf(f, "track-to-track seek: %ld\t# milliseconds\n",
591 		    (u_long)lp->d_trkseek);
592 		fprintf(f, "drivedata: ");
593 		for (i = NDDATA - 1; i >= 0; i--)
594 			if (lp->d_drivedata[i])
595 				break;
596 		if (i < 0)
597 			i = 0;
598 		for (j = 0; j <= i; j++)
599 			fprintf(f, "%lu ", (u_long)lp->d_drivedata[j]);
600 		fprintf(f, "\n\n");
601 	}
602 	fprintf(f, "%u partitions:\n", lp->d_npartitions);
603 	fprintf(f,
604 	    "#          size     offset    fstype   [fsize bsize bps/cpg]\n");
605 	pp = lp->d_partitions;
606 	for (i = 0; i < lp->d_npartitions; i++, pp++) {
607 		if (pp->p_size) {
608 			fprintf(f, "  %c: %10lu %10lu  ", 'a' + i,
609 			   (u_long)pp->p_size, (u_long)pp->p_offset);
610 			if (pp->p_fstype < FSMAXTYPES)
611 				fprintf(f, "%8.8s", fstypenames[pp->p_fstype]);
612 			else
613 				fprintf(f, "%8d", pp->p_fstype);
614 			switch (pp->p_fstype) {
615 
616 			case FS_UNUSED:				/* XXX */
617 				fprintf(f, "    %5lu %5lu %2s",
618 				    (u_long)pp->p_fsize,
619 				    (u_long)(pp->p_fsize * pp->p_frag), "");
620 				break;
621 
622 			case FS_BSDFFS:
623 				fprintf(f, "    %5lu %5lu %5u",
624 				    (u_long)pp->p_fsize,
625 				    (u_long)(pp->p_fsize * pp->p_frag),
626 				    pp->p_cpg);
627 				break;
628 
629 			case FS_BSDLFS:
630 				fprintf(f, "    %5lu %5lu %5d",
631 				    (u_long)pp->p_fsize,
632 				    (u_long)(pp->p_fsize * pp->p_frag),
633 				    pp->p_cpg);
634 				break;
635 
636 			default:
637 				fprintf(f, "%20.20s", "");
638 				break;
639 			}
640 			if (i == RAW_PART) {
641 				fprintf(f, "  # \"raw\" part, don't edit");
642 			}
643 			fprintf(f, "\n");
644 		}
645 	}
646 	fflush(f);
647 }
648 
649 static int
650 edit(void)
651 {
652 	int c, fd;
653 	struct disklabel label;
654 	FILE *fp;
655 
656 	if ((fd = mkstemp(tmpfil)) == -1 ||
657 	    (fp = fdopen(fd, "w")) == NULL) {
658 		warnx("can't create %s", tmpfil);
659 		return (1);
660 	}
661 	display(fp, NULL);
662 	fclose(fp);
663 	for (;;) {
664 		if (!editit())
665 			break;
666 		fp = fopen(tmpfil, "r");
667 		if (fp == NULL) {
668 			warnx("can't reopen %s for reading", tmpfil);
669 			break;
670 		}
671 		bzero((char *)&label, sizeof(label));
672 		c = getasciilabel(fp, &label);
673 		fclose(fp);
674 		if (c) {
675 			lab = label;
676 			if (writelabel() == 0) {
677 				(void) unlink(tmpfil);
678 				return (0);
679 			}
680 		}
681 		printf("re-edit the label? [y]: ");
682 		fflush(stdout);
683 		c = getchar();
684 		if (c != EOF && c != (int)'\n')
685 			while (getchar() != (int)'\n')
686 				;
687 		if  (c == (int)'n')
688 			break;
689 	}
690 	(void) unlink(tmpfil);
691 	return (1);
692 }
693 
694 static int
695 editit(void)
696 {
697 	int pid, xpid;
698 	int locstat, omask;
699 	const char *ed;
700 	uid_t uid;
701 	gid_t gid;
702 
703 	omask = sigblock(sigmask(SIGINT)|sigmask(SIGQUIT)|sigmask(SIGHUP));
704 	while ((pid = fork()) < 0) {
705 		if (errno == EPROCLIM) {
706 			warnx("you have too many processes");
707 			return(0);
708 		}
709 		if (errno != EAGAIN) {
710 			warn("fork");
711 			return(0);
712 		}
713 		sleep(1);
714 	}
715 	if (pid == 0) {
716 		sigsetmask(omask);
717 		gid = getgid();
718 		if (setresgid(gid, gid, gid) == -1)
719 			err(1, "setresgid");
720 		uid = getuid();
721 		if (setresuid(uid, uid, uid) == -1)
722 			err(1, "setresuid");
723 		if ((ed = getenv("EDITOR")) == (char *)0)
724 			ed = DEFEDITOR;
725 		execlp(ed, ed, tmpfil, (char *)0);
726 		err(1, "%s", ed);
727 	}
728 	while ((xpid = wait(&locstat)) >= 0)
729 		if (xpid == pid)
730 			break;
731 	sigsetmask(omask);
732 	return(!locstat);
733 }
734 
735 static char *
736 skip(char *cp)
737 {
738 
739 	while (*cp != '\0' && isspace(*cp))
740 		cp++;
741 	if (*cp == '\0' || *cp == '#')
742 		return (NULL);
743 	return (cp);
744 }
745 
746 static char *
747 word(char *cp)
748 {
749 	char c;
750 
751 	while (*cp != '\0' && !isspace(*cp) && *cp != '#')
752 		cp++;
753 	if ((c = *cp) != '\0') {
754 		*cp++ = '\0';
755 		if (c != '#')
756 			return (skip(cp));
757 	}
758 	return (NULL);
759 }
760 
761 /*
762  * Read an ascii label in from fd f,
763  * in the same format as that put out by display(),
764  * and fill in lp.
765  */
766 static int
767 getasciilabel(FILE *f, struct disklabel *lp)
768 {
769 	char *cp, *endp;
770 	const char **cpp;
771 	u_int part;
772 	char *tp, line[BUFSIZ];
773 	u_long v;
774 	int lineno = 0, errors = 0;
775 	int i;
776 
777 	makelabel("auto", lp);
778 	bzero(&part_set, sizeof(part_set));
779 	bzero(&part_size_type, sizeof(part_size_type));
780 	bzero(&part_offset_type, sizeof(part_offset_type));
781 	lp->d_bbsize = BBSIZE;				/* XXX */
782 	lp->d_sbsize = 0;				/* XXX */
783 	while (fgets(line, sizeof(line) - 1, f)) {
784 		lineno++;
785 		if ((cp = strchr(line,'\n')) != NULL)
786 			*cp = '\0';
787 		cp = skip(line);
788 		if (cp == NULL)
789 			continue;
790 		tp = strchr(cp, ':');
791 		if (tp == NULL) {
792 			fprintf(stderr, "line %d: syntax error\n", lineno);
793 			errors++;
794 			continue;
795 		}
796 		*tp++ = '\0', tp = skip(tp);
797 		if (!strcmp(cp, "type")) {
798 			if (tp == NULL)
799 				tp = unknown;
800 			cpp = dktypenames;
801 			for (; cpp < &dktypenames[DKMAXTYPES]; cpp++)
802 				if (*cpp && !strcmp(*cpp, tp)) {
803 					lp->d_type = cpp - dktypenames;
804 					break;
805 				}
806 			if (cpp < &dktypenames[DKMAXTYPES])
807 				continue;
808 			errno = 0;
809 			v = strtoul(tp, &endp, 10);
810 			if (errno != 0 || *endp != '\0')
811 				v = DKMAXTYPES;
812 			if (v >= DKMAXTYPES)
813 				fprintf(stderr, "line %d:%s %lu\n", lineno,
814 				    "Warning, unknown disk type", v);
815 			else
816 				lp->d_type = v;
817 			continue;
818 		}
819 		if (!strcmp(cp, "flags")) {
820 			for (v = 0; (cp = tp) && *cp != '\0';) {
821 				tp = word(cp);
822 				if (!strcmp(cp, "removeable"))
823 					v |= D_REMOVABLE;
824 				else if (!strcmp(cp, "ecc"))
825 					v |= D_ECC;
826 				else if (!strcmp(cp, "badsect"))
827 					v |= D_BADSECT;
828 				else {
829 					fprintf(stderr,
830 					    "line %d: %s: bad flag\n",
831 					    lineno, cp);
832 					errors++;
833 				}
834 			}
835 			lp->d_flags = v;
836 			continue;
837 		}
838 		if (!strcmp(cp, "drivedata")) {
839 			for (i = 0; (cp = tp) && *cp != '\0' && i < NDDATA;) {
840 				lp->d_drivedata[i++] = strtoul(cp, NULL, 10);
841 				tp = word(cp);
842 			}
843 			continue;
844 		}
845 		if (sscanf(cp, "%lu partitions", &v) == 1) {
846 			if (v > MAXPARTITIONS) {
847 				fprintf(stderr,
848 				    "line %d: bad # of partitions\n", lineno);
849 				lp->d_npartitions = MAXPARTITIONS;
850 				errors++;
851 			} else if (v < DEFPARTITIONS) {
852 				fprintf(stderr,
853 				    "line %d: bad # of partitions\n", lineno);
854 				lp->d_npartitions = DEFPARTITIONS;
855 				errors++;
856 			} else
857 				lp->d_npartitions = v;
858 			continue;
859 		}
860 		if (tp == NULL)
861 			tp = blank;
862 		if (!strcmp(cp, "disk")) {
863 			strncpy(lp->d_typename, tp, sizeof (lp->d_typename));
864 			continue;
865 		}
866 		if (!strcmp(cp, "label")) {
867 			strncpy(lp->d_packname, tp, sizeof (lp->d_packname));
868 			continue;
869 		}
870 		if (!strcmp(cp, "bytes/sector")) {
871 			v = strtoul(tp, NULL, 10);
872 			if (v == 0 || (v % DEV_BSIZE) != 0) {
873 				fprintf(stderr,
874 				    "line %d: %s: bad sector size\n",
875 				    lineno, tp);
876 				errors++;
877 			} else
878 				lp->d_secsize = v;
879 			continue;
880 		}
881 		if (!strcmp(cp, "sectors/track")) {
882 			v = strtoul(tp, NULL, 10);
883 #if (ULONG_MAX != 0xffffffffUL)
884 			if (v == 0 || v > 0xffffffff)
885 #else
886 			if (v == 0)
887 #endif
888 			{
889 				fprintf(stderr, "line %d: %s: bad %s\n",
890 				    lineno, tp, cp);
891 				errors++;
892 			} else
893 				lp->d_nsectors = v;
894 			continue;
895 		}
896 		if (!strcmp(cp, "sectors/cylinder")) {
897 			v = strtoul(tp, NULL, 10);
898 			if (v == 0) {
899 				fprintf(stderr, "line %d: %s: bad %s\n",
900 				    lineno, tp, cp);
901 				errors++;
902 			} else
903 				lp->d_secpercyl = v;
904 			continue;
905 		}
906 		if (!strcmp(cp, "tracks/cylinder")) {
907 			v = strtoul(tp, NULL, 10);
908 			if (v == 0) {
909 				fprintf(stderr, "line %d: %s: bad %s\n",
910 				    lineno, tp, cp);
911 				errors++;
912 			} else
913 				lp->d_ntracks = v;
914 			continue;
915 		}
916 		if (!strcmp(cp, "cylinders")) {
917 			v = strtoul(tp, NULL, 10);
918 			if (v == 0) {
919 				fprintf(stderr, "line %d: %s: bad %s\n",
920 				    lineno, tp, cp);
921 				errors++;
922 			} else
923 				lp->d_ncylinders = v;
924 			continue;
925 		}
926 		if (!strcmp(cp, "sectors/unit")) {
927 			v = strtoul(tp, NULL, 10);
928 			if (v == 0) {
929 				fprintf(stderr, "line %d: %s: bad %s\n",
930 				    lineno, tp, cp);
931 				errors++;
932 			} else
933 				lp->d_secperunit = v;
934 			continue;
935 		}
936 		if (!strcmp(cp, "rpm")) {
937 			v = strtoul(tp, NULL, 10);
938 			if (v == 0 || v > USHRT_MAX) {
939 				fprintf(stderr, "line %d: %s: bad %s\n",
940 				    lineno, tp, cp);
941 				errors++;
942 			} else
943 				lp->d_rpm = v;
944 			continue;
945 		}
946 		if (!strcmp(cp, "interleave")) {
947 			v = strtoul(tp, NULL, 10);
948 			if (v == 0 || v > USHRT_MAX) {
949 				fprintf(stderr, "line %d: %s: bad %s\n",
950 				    lineno, tp, cp);
951 				errors++;
952 			} else
953 				lp->d_interleave = v;
954 			continue;
955 		}
956 		if (!strcmp(cp, "trackskew")) {
957 			v = strtoul(tp, NULL, 10);
958 			if (v > USHRT_MAX) {
959 				fprintf(stderr, "line %d: %s: bad %s\n",
960 				    lineno, tp, cp);
961 				errors++;
962 			} else
963 				lp->d_trackskew = v;
964 			continue;
965 		}
966 		if (!strcmp(cp, "cylinderskew")) {
967 			v = strtoul(tp, NULL, 10);
968 			if (v > USHRT_MAX) {
969 				fprintf(stderr, "line %d: %s: bad %s\n",
970 				    lineno, tp, cp);
971 				errors++;
972 			} else
973 				lp->d_cylskew = v;
974 			continue;
975 		}
976 		if (!strcmp(cp, "headswitch")) {
977 			v = strtoul(tp, NULL, 10);
978 			lp->d_headswitch = v;
979 			continue;
980 		}
981 		if (!strcmp(cp, "track-to-track seek")) {
982 			v = strtoul(tp, NULL, 10);
983 			lp->d_trkseek = v;
984 			continue;
985 		}
986 		/* the ':' was removed above */
987 		if (*cp < 'a' || *cp > MAX_PART || cp[1] != '\0') {
988 			fprintf(stderr,
989 			    "line %d: %s: Unknown disklabel field\n", lineno,
990 			    cp);
991 			errors++;
992 			continue;
993 		}
994 
995 		/* Process a partition specification line. */
996 		part = *cp - 'a';
997 		if (part >= lp->d_npartitions) {
998 			fprintf(stderr,
999 			    "line %d: partition name out of range a-%c: %s\n",
1000 			    lineno, 'a' + lp->d_npartitions - 1, cp);
1001 			errors++;
1002 			continue;
1003 		}
1004 		part_set[part] = 1;
1005 
1006 		if (getasciipartspec(tp, lp, part, lineno) != 0) {
1007 			errors++;
1008 			break;
1009 		}
1010 	}
1011 	errors += checklabel(lp);
1012 	return (errors == 0);
1013 }
1014 
1015 #define NXTNUM(n) do { \
1016 	if (tp == NULL) { \
1017 		fprintf(stderr, "line %d: too few numeric fields\n", lineno); \
1018 		return (1); \
1019 	} else { \
1020 		cp = tp, tp = word(cp); \
1021 		(n) = strtoul(cp, NULL, 10); \
1022 	} \
1023 } while (0)
1024 
1025 /* retain 1 character following number */
1026 #define NXTWORD(w,n) do { \
1027 	if (tp == NULL) { \
1028 		fprintf(stderr, "line %d: too few numeric fields\n", lineno); \
1029 		return (1); \
1030 	} else { \
1031 		char *tmp; \
1032 		cp = tp, tp = word(cp); \
1033 		(n) = strtoul(cp, &tmp, 10); \
1034 		if (tmp) (w) = *tmp; \
1035 	} \
1036 } while (0)
1037 
1038 /*
1039  * Read a partition line into partition `part' in the specified disklabel.
1040  * Return 0 on success, 1 on failure.
1041  */
1042 static int
1043 getasciipartspec(char *tp, struct disklabel *lp, int part, int lineno)
1044 {
1045 	struct partition *pp;
1046 	char *cp, *endp;
1047 	const char **cpp;
1048 	u_long v;
1049 
1050 	pp = &lp->d_partitions[part];
1051 	cp = NULL;
1052 
1053 	v = 0;
1054 	NXTWORD(part_size_type[part],v);
1055 	if (v == 0 && part_size_type[part] != '*') {
1056 		fprintf(stderr,
1057 		    "line %d: %s: bad partition size\n", lineno, cp);
1058 		return (1);
1059 	}
1060 	pp->p_size = v;
1061 
1062 	v = 0;
1063 	NXTWORD(part_offset_type[part],v);
1064 	if (v == 0 && part_offset_type[part] != '*' &&
1065 	    part_offset_type[part] != '\0') {
1066 		fprintf(stderr,
1067 		    "line %d: %s: bad partition offset\n", lineno, cp);
1068 		return (1);
1069 	}
1070 	pp->p_offset = v;
1071 	if (tp == NULL) {
1072 		fprintf(stderr, "line %d: missing file system type\n", lineno);
1073 		return (1);
1074 	}
1075 	cp = tp, tp = word(cp);
1076 	for (cpp = fstypenames; cpp < &fstypenames[FSMAXTYPES]; cpp++)
1077 		if (*cpp && !strcmp(*cpp, cp))
1078 			break;
1079 	if (*cpp != NULL) {
1080 		pp->p_fstype = cpp - fstypenames;
1081 	} else {
1082 		if (isdigit(*cp)) {
1083 			errno = 0;
1084 			v = strtoul(cp, &endp, 10);
1085 			if (errno != 0 || *endp != '\0')
1086 				v = FSMAXTYPES;
1087 		} else
1088 			v = FSMAXTYPES;
1089 		if (v >= FSMAXTYPES) {
1090 			fprintf(stderr,
1091 			    "line %d: Warning, unknown file system type %s\n",
1092 			    lineno, cp);
1093 			v = FS_UNUSED;
1094 		}
1095 		pp->p_fstype = v;
1096 	}
1097 
1098 	switch (pp->p_fstype) {
1099 	case FS_UNUSED:
1100 	case FS_BSDFFS:
1101 	case FS_BSDLFS:
1102 		/* accept defaults for fsize/frag/cpg */
1103 		if (tp) {
1104 			NXTNUM(pp->p_fsize);
1105 			if (pp->p_fsize == 0)
1106 				break;
1107 			NXTNUM(v);
1108 			pp->p_frag = v / pp->p_fsize;
1109 			if (tp != NULL)
1110 				NXTNUM(pp->p_cpg);
1111 		}
1112 		/* else default to 0's */
1113 		break;
1114 	default:
1115 		break;
1116 	}
1117 	return (0);
1118 }
1119 
1120 /*
1121  * Check disklabel for errors and fill in
1122  * derived fields according to supplied values.
1123  */
1124 static int
1125 checklabel(struct disklabel *lp)
1126 {
1127 	struct partition *pp;
1128 	int i, errors = 0;
1129 	char part;
1130 	u_long base_offset, needed, total_size, total_percent, current_offset;
1131 	long free_space;
1132 	int seen_default_offset;
1133 	int hog_part;
1134 	int j;
1135 	struct partition *pp2;
1136 
1137 	if (lp == NULL)
1138 		lp = &lab;
1139 
1140 	if (allfields) {
1141 
1142 		if (lp->d_secsize == 0) {
1143 			fprintf(stderr, "sector size 0\n");
1144 			return (1);
1145 		}
1146 		if (lp->d_nsectors == 0) {
1147 			fprintf(stderr, "sectors/track 0\n");
1148 			return (1);
1149 		}
1150 		if (lp->d_ntracks == 0) {
1151 			fprintf(stderr, "tracks/cylinder 0\n");
1152 			return (1);
1153 		}
1154 		if  (lp->d_ncylinders == 0) {
1155 			fprintf(stderr, "cylinders/unit 0\n");
1156 			errors++;
1157 		}
1158 		if (lp->d_rpm == 0)
1159 			warnx("revolutions/minute 0");
1160 		if (lp->d_secpercyl == 0)
1161 			lp->d_secpercyl = lp->d_nsectors * lp->d_ntracks;
1162 		if (lp->d_secperunit == 0)
1163 			lp->d_secperunit = lp->d_secpercyl * lp->d_ncylinders;
1164 		if (lp->d_bbsize == 0) {
1165 			fprintf(stderr, "boot block size 0\n");
1166 			errors++;
1167 		} else if (lp->d_bbsize % lp->d_secsize)
1168 			warnx("boot block size %% sector-size != 0");
1169 		if (lp->d_npartitions > MAXPARTITIONS) {
1170 			warnx("number of partitions (%lu) > MAXPARTITIONS (%d)",
1171 			    (u_long)lp->d_npartitions, MAXPARTITIONS);
1172 			errors++;
1173 		}
1174 		if (lp->d_npartitions < DEFPARTITIONS) {
1175 			warnx("number of partitions (%lu) < DEFPARTITIONS (%d)",
1176 			    (u_long)lp->d_npartitions, DEFPARTITIONS);
1177 			errors++;
1178 		}
1179 	} else {
1180 		struct disklabel *vl;
1181 
1182 		vl = getvirginlabel();
1183 		if (lp->d_secsize == 0)
1184 			lp->d_secsize = vl->d_secsize;
1185 		if (lp->d_nsectors == 0)
1186 			lp->d_nsectors = vl->d_nsectors;
1187 		if (lp->d_ntracks == 0)
1188 			lp->d_ntracks = vl->d_ntracks;
1189 		if (lp->d_ncylinders == 0)
1190 			lp->d_ncylinders = vl->d_ncylinders;
1191 		if (lp->d_rpm == 0)
1192 			lp->d_rpm = vl->d_rpm;
1193 		if (lp->d_interleave == 0)
1194 			lp->d_interleave = vl->d_interleave;
1195 		if (lp->d_secpercyl == 0)
1196 			lp->d_secpercyl = vl->d_secpercyl;
1197 		if (lp->d_secperunit == 0 ||
1198 		    lp->d_secperunit > vl->d_secperunit)
1199 			lp->d_secperunit = vl->d_secperunit;
1200 		if (lp->d_bbsize == 0)
1201 			lp->d_bbsize = vl->d_bbsize;
1202 		if (lp->d_npartitions < DEFPARTITIONS ||
1203 		    lp->d_npartitions > MAXPARTITIONS)
1204 			lp->d_npartitions = vl->d_npartitions;
1205 	}
1206 
1207 
1208 	/* first allocate space to the partitions, then offsets */
1209 	total_size = 0; /* in sectors */
1210 	total_percent = 0; /* in percent */
1211 	hog_part = -1;
1212 	/* find all fixed partitions */
1213 	for (i = 0; i < lp->d_npartitions; i++) {
1214 		pp = &lp->d_partitions[i];
1215 		if (part_set[i]) {
1216 			if (part_size_type[i] == '*') {
1217 				if (i == RAW_PART) {
1218 					pp->p_size = lp->d_secperunit;
1219 				} else {
1220 					if (hog_part != -1)
1221 						warnx("Too many '*' partitions (%c and %c)",
1222 						    hog_part + 'a',i + 'a');
1223 					else
1224 						hog_part = i;
1225 				}
1226 			} else {
1227 				off_t size;
1228 
1229 				size = pp->p_size;
1230 				switch (part_size_type[i]) {
1231 				case '%':
1232 					total_percent += size;
1233 					break;
1234 				case 't':
1235 				case 'T':
1236 					size *= 1024ULL;
1237 					/* FALLTHROUGH */
1238 				case 'g':
1239 				case 'G':
1240 					size *= 1024ULL;
1241 					/* FALLTHROUGH */
1242 				case 'm':
1243 				case 'M':
1244 					size *= 1024ULL;
1245 					/* FALLTHROUGH */
1246 				case 'k':
1247 				case 'K':
1248 					size *= 1024ULL;
1249 					break;
1250 				case '\0':
1251 					break;
1252 				default:
1253 					warnx("unknown multiplier suffix '%c' for partition %c (should be K, M, G or T)",
1254 					    part_size_type[i], i + 'a');
1255 					break;
1256 				}
1257 				/* don't count %'s yet */
1258 				if (part_size_type[i] != '%') {
1259 					/*
1260 					 * for all not in sectors, convert to
1261 					 * sectors
1262 					 */
1263 					if (part_size_type[i] != '\0') {
1264 						if (size % lp->d_secsize != 0)
1265 							warnx("partition %c not an integer number of sectors",
1266 							    i + 'a');
1267 						size /= lp->d_secsize;
1268 						pp->p_size = size;
1269 					}
1270 					/* else already in sectors */
1271 					if (i != RAW_PART)
1272 						total_size += size;
1273 				}
1274 			}
1275 		}
1276 	}
1277 
1278 	/* Find out the total free space, excluding the boot block area. */
1279 	base_offset = BBSIZE / secsize;
1280 	free_space = 0;
1281 	for (i = 0; i < lp->d_npartitions; i++) {
1282 		pp = &lp->d_partitions[i];
1283 		if (!part_set[i] || i == RAW_PART ||
1284 		    part_size_type[i] == '%' || part_size_type[i] == '*')
1285 			continue;
1286 		if (pp->p_offset > base_offset)
1287 			free_space += pp->p_offset - base_offset;
1288 		if (pp->p_offset + pp->p_size > base_offset)
1289 			base_offset = pp->p_offset + pp->p_size;
1290 	}
1291 	if (base_offset < lp->d_secperunit)
1292 		free_space += lp->d_secperunit - base_offset;
1293 
1294 	/* handle % partitions - note %'s don't need to add up to 100! */
1295 	if (total_percent != 0) {
1296 		if (total_percent > 100) {
1297 			fprintf(stderr,"total percentage %lu is greater than 100\n",
1298 			    total_percent);
1299 			errors++;
1300 		}
1301 
1302 		if (free_space > 0) {
1303 			for (i = 0; i < lp->d_npartitions; i++) {
1304 				pp = &lp->d_partitions[i];
1305 				if (part_set[i] && part_size_type[i] == '%') {
1306 					/* careful of overflows! and integer roundoff */
1307 					pp->p_size = ((double)pp->p_size/100) * free_space;
1308 					total_size += pp->p_size;
1309 
1310 					/* FIX we can lose a sector or so due to roundoff per
1311 					   partition.  A more complex algorithm could avoid that */
1312 				}
1313 			}
1314 		} else {
1315 			fprintf(stderr,
1316 			    "%ld sectors available to give to '*' and '%%' partitions\n",
1317 			    free_space);
1318 			errors++;
1319 			/* fix?  set all % partitions to size 0? */
1320 		}
1321 	}
1322 	/* give anything remaining to the hog partition */
1323 	if (hog_part != -1) {
1324 		/*
1325 		 * Find the range of offsets usable by '*' partitions around
1326 		 * the hog partition and how much space they need.
1327 		 */
1328 		needed = 0;
1329 		base_offset = BBSIZE / secsize;
1330 		for (i = hog_part - 1; i >= 0; i--) {
1331 			pp = &lp->d_partitions[i];
1332 			if (!part_set[i] || i == RAW_PART)
1333 				continue;
1334 			if (part_offset_type[i] == '*') {
1335 				needed += pp->p_size;
1336 				continue;
1337 			}
1338 			base_offset = pp->p_offset + pp->p_size;
1339 			break;
1340 		}
1341 		current_offset = lp->d_secperunit;
1342 		for (i = lp->d_npartitions - 1; i > hog_part; i--) {
1343 			pp = &lp->d_partitions[i];
1344 			if (!part_set[i] || i == RAW_PART)
1345 				continue;
1346 			if (part_offset_type[i] == '*') {
1347 				needed += pp->p_size;
1348 				continue;
1349 			}
1350 			current_offset = pp->p_offset;
1351 		}
1352 
1353 		if (current_offset - base_offset <= needed) {
1354 			fprintf(stderr, "Cannot find space for partition %c\n",
1355 			    hog_part + 'a');
1356 			fprintf(stderr,
1357 			    "Need more than %lu sectors between %lu and %lu\n",
1358 			    needed, base_offset, current_offset);
1359 			errors++;
1360 			lp->d_partitions[hog_part].p_size = 0;
1361 		} else {
1362 			lp->d_partitions[hog_part].p_size = current_offset -
1363 			    base_offset - needed;
1364 			total_size += lp->d_partitions[hog_part].p_size;
1365 		}
1366 	}
1367 
1368 	/* Now set the offsets for each partition */
1369 	current_offset = BBSIZE / secsize; /* in sectors */
1370 	seen_default_offset = 0;
1371 	for (i = 0; i < lp->d_npartitions; i++) {
1372 		part = 'a' + i;
1373 		pp = &lp->d_partitions[i];
1374 		if (part_set[i]) {
1375 			if (part_offset_type[i] == '*') {
1376 				if (i == RAW_PART) {
1377 					pp->p_offset = 0;
1378 				} else {
1379 					pp->p_offset = current_offset;
1380 					seen_default_offset = 1;
1381 				}
1382 			} else {
1383 				/* allow them to be out of order for old-style tables */
1384 				if (pp->p_offset < current_offset &&
1385 				    seen_default_offset && i != RAW_PART &&
1386 				    pp->p_fstype != FS_VINUM) {
1387 					fprintf(stderr,
1388 "Offset %ld for partition %c overlaps previous partition which ends at %lu\n",
1389 					    (long)pp->p_offset,i+'a',current_offset);
1390 					fprintf(stderr,
1391 "Labels with any *'s for offset must be in ascending order by sector\n");
1392 					errors++;
1393 				} else if (pp->p_offset != current_offset &&
1394 				    i != RAW_PART && seen_default_offset) {
1395 					/*
1396 					 * this may give unneeded warnings if
1397 					 * partitions are out-of-order
1398 					 */
1399 					warnx(
1400 "Offset %ld for partition %c doesn't match expected value %ld",
1401 					    (long)pp->p_offset, i + 'a', current_offset);
1402 				}
1403 			}
1404 			if (i != RAW_PART)
1405 				current_offset = pp->p_offset + pp->p_size;
1406 		}
1407 	}
1408 
1409 	for (i = 0; i < lp->d_npartitions; i++) {
1410 		part = 'a' + i;
1411 		pp = &lp->d_partitions[i];
1412 		if (pp->p_size == 0 && pp->p_offset != 0)
1413 			warnx("partition %c: size 0, but offset %lu",
1414 			    part, (u_long)pp->p_offset);
1415 #ifdef notdef
1416 		if (pp->p_size % lp->d_secpercyl)
1417 			warnx("partition %c: size %% cylinder-size != 0",
1418 			    part);
1419 		if (pp->p_offset % lp->d_secpercyl)
1420 			warnx("partition %c: offset %% cylinder-size != 0",
1421 			    part);
1422 #endif
1423 		if (pp->p_offset > lp->d_secperunit) {
1424 			fprintf(stderr,
1425 			    "partition %c: offset past end of unit\n", part);
1426 			errors++;
1427 		}
1428 		if (pp->p_offset + pp->p_size > lp->d_secperunit) {
1429 			fprintf(stderr,
1430 			"partition %c: partition extends past end of unit\n",
1431 			    part);
1432 			errors++;
1433 		}
1434 		if (i == RAW_PART) {
1435 			if (pp->p_fstype != FS_UNUSED)
1436 				warnx("partition %c is not marked as unused!",part);
1437 			if (pp->p_offset != 0)
1438 				warnx("partition %c doesn't start at 0!",part);
1439 			if (pp->p_size != lp->d_secperunit)
1440 				warnx("partition %c doesn't cover the whole unit!",part);
1441 
1442 			if ((pp->p_fstype != FS_UNUSED) || (pp->p_offset != 0) ||
1443 			    (pp->p_size != lp->d_secperunit)) {
1444 				warnx("An incorrect partition %c may cause problems for "
1445 				    "standard system utilities",part);
1446 			}
1447 		}
1448 
1449 		/* check for overlaps */
1450 		/* this will check for all possible overlaps once and only once */
1451 		for (j = 0; j < i; j++) {
1452 			pp2 = &lp->d_partitions[j];
1453 			if (j != RAW_PART && i != RAW_PART &&
1454 			    pp->p_fstype != FS_VINUM &&
1455 			    pp2->p_fstype != FS_VINUM &&
1456 			    part_set[i] && part_set[j]) {
1457 				if (pp2->p_offset < pp->p_offset + pp->p_size &&
1458 				    (pp2->p_offset + pp2->p_size > pp->p_offset ||
1459 					pp2->p_offset >= pp->p_offset)) {
1460 					fprintf(stderr,"partitions %c and %c overlap!\n",
1461 					    j + 'a', i + 'a');
1462 					errors++;
1463 				}
1464 			}
1465 		}
1466 	}
1467 	for (; i < lp->d_npartitions; i++) {
1468 		part = 'a' + i;
1469 		pp = &lp->d_partitions[i];
1470 		if (pp->p_size || pp->p_offset)
1471 			warnx("unused partition %c: size %d offset %lu",
1472 			    'a' + i, pp->p_size, (u_long)pp->p_offset);
1473 	}
1474 	return (errors);
1475 }
1476 
1477 /*
1478  * When operating on a "virgin" disk, try getting an initial label
1479  * from the associated device driver.  This might work for all device
1480  * drivers that are able to fetch some initial device parameters
1481  * without even having access to a (BSD) disklabel, like SCSI disks,
1482  * most IDE drives, or vn devices.
1483  *
1484  * The device name must be given in its "canonical" form.
1485  */
1486 static struct disklabel *
1487 getvirginlabel(void)
1488 {
1489 	static struct disklabel loclab;
1490 	struct partition *dp;
1491 	int f;
1492 	u_int u;
1493 
1494 	if ((f = open(specname, O_RDONLY)) == -1) {
1495 		warn("cannot open %s", specname);
1496 		return (NULL);
1497 	}
1498 
1499 	if (is_file)
1500 		get_file_parms(f);
1501 	else {
1502 		mediasize = g_mediasize(f);
1503 		secsize = g_sectorsize(f);
1504 		if (secsize < 0 || mediasize < 0) {
1505 			close (f);
1506 			return (NULL);
1507 		}
1508 	}
1509 	memset(&loclab, 0, sizeof loclab);
1510 	loclab.d_magic = DISKMAGIC;
1511 	loclab.d_magic2 = DISKMAGIC;
1512 	loclab.d_secsize = secsize;
1513 	loclab.d_secperunit = mediasize / secsize;
1514 
1515 	/*
1516 	 * Nobody in these enlightened days uses the CHS geometry for
1517 	 * anything, but nonetheless try to get it right.  If we fail
1518 	 * to get any good ideas from the device, construct something
1519 	 * which is IBM-PC friendly.
1520 	 */
1521 	if (ioctl(f, DIOCGFWSECTORS, &u) == 0)
1522 		loclab.d_nsectors = u;
1523 	else
1524 		loclab.d_nsectors = 63;
1525 	if (ioctl(f, DIOCGFWHEADS, &u) == 0)
1526 		loclab.d_ntracks = u;
1527 	else if (loclab.d_secperunit <= 63*1*1024)
1528 		loclab.d_ntracks = 1;
1529 	else if (loclab.d_secperunit <= 63*16*1024)
1530 		loclab.d_ntracks = 16;
1531 	else
1532 		loclab.d_ntracks = 255;
1533 	loclab.d_secpercyl = loclab.d_ntracks * loclab.d_nsectors;
1534 	loclab.d_ncylinders = loclab.d_secperunit / loclab.d_secpercyl;
1535 	loclab.d_npartitions = DEFPARTITIONS;
1536 
1537 	/* Various (unneeded) compat stuff */
1538 	loclab.d_rpm = 3600;
1539 	loclab.d_bbsize = BBSIZE;
1540 	loclab.d_interleave = 1;
1541 	strncpy(loclab.d_typename, "amnesiac",
1542 	    sizeof(loclab.d_typename));
1543 
1544 	dp = &loclab.d_partitions[RAW_PART];
1545 	dp->p_size = loclab.d_secperunit;
1546 	loclab.d_checksum = dkcksum(&loclab);
1547 	close (f);
1548 	return (&loclab);
1549 }
1550 
1551 static void
1552 usage(void)
1553 {
1554 
1555 	fprintf(stderr,
1556 	"%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",
1557 	"usage: bsdlabel disk",
1558 	"\t\t(to read label)",
1559 	"	bsdlabel -w [-n] [-m machine] disk [type]",
1560 	"\t\t(to write label with existing boot program)",
1561 	"	bsdlabel -e [-n] [-m machine] disk",
1562 	"\t\t(to edit label)",
1563 	"	bsdlabel -R [-n] [-m machine] disk protofile",
1564 	"\t\t(to restore label with existing boot program)",
1565 	"	bsdlabel -B [-b boot] [-m machine] disk",
1566 	"\t\t(to install boot program with existing on-disk label)",
1567 	"	bsdlabel -w -B [-n] [-b boot] [-m machine] disk [type]",
1568 	"\t\t(to write label and install boot program)",
1569 	"	bsdlabel -R -B [-n] [-b boot] [-m machine] disk protofile",
1570 		"\t\t(to restore label and install boot program)"
1571 	);
1572 	exit(1);
1573 }
1574