xref: /freebsd/sbin/bsdlabel/bsdlabel.c (revision 6be3386466ab79a84b48429ae66244f21526d3df)
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 disklabel *lp = &lab;
385 
386 	if (disable_write) {
387 		warnx("write to disk label suppressed - label was as follows:");
388 		display(stdout, NULL);
389 		return (0);
390 	}
391 
392 	lp->d_magic = DISKMAGIC;
393 	lp->d_magic2 = DISKMAGIC;
394 	lp->d_checksum = 0;
395 	lp->d_checksum = dkcksum(lp);
396 	if (installboot)
397 		readboot();
398 	for (i = 0; i < lab.d_npartitions; i++)
399 		if (lab.d_partitions[i].p_size)
400 			lab.d_partitions[i].p_offset += lba_offset;
401 	bsd_disklabel_le_enc(bootarea + labeloffset + labelsoffset * lab.d_secsize,
402 	    lp);
403 
404 	fd = open(specname, O_RDWR);
405 	if (fd < 0) {
406 		if (is_file) {
407 			warn("cannot open file %s for writing label", specname);
408 			return(1);
409 		} else
410 			serrno = errno;
411 
412 		if (geom_class_available("PART") != 0) {
413 			/*
414 			 * Since we weren't able open provider for
415 			 * writing, then recommend user to use gpart(8).
416 			 */
417 			warnc(serrno,
418 			    "cannot open provider %s for writing label",
419 			    specname);
420 			warnx("Try to use gpart(8).");
421 			return (1);
422 		}
423 
424 		warnc(serrno, "%s", specname);
425 		return (1);
426 	} else {
427 		if (write(fd, bootarea, bbsize) != bbsize) {
428 			warn("write %s", specname);
429 			close (fd);
430 			return (1);
431 		}
432 		close (fd);
433 	}
434 	return (0);
435 }
436 
437 static void
438 get_file_parms(int f)
439 {
440 	int i;
441 	struct stat sb;
442 
443 	if (fstat(f, &sb) != 0)
444 		err(4, "fstat failed");
445 	i = sb.st_mode & S_IFMT;
446 	if (i != S_IFREG && i != S_IFLNK)
447 		errx(4, "%s is not a valid file or link", specname);
448 	secsize = DEV_BSIZE;
449 	mediasize = sb.st_size;
450 }
451 
452 /*
453  * Fetch disklabel for disk.
454  */
455 static int
456 readlabel(int flag)
457 {
458 	ssize_t nbytes;
459 	uint32_t lba;
460 	int f, i;
461 	int error;
462 
463 	f = open(specname, O_RDONLY);
464 	if (f < 0)
465 		err(1, "%s", specname);
466 	if (is_file)
467 		get_file_parms(f);
468 	else {
469 		mediasize = g_mediasize(f);
470 		secsize = g_sectorsize(f);
471 		if (secsize < 0 || mediasize < 0)
472 			err(4, "cannot get disk geometry");
473 	}
474 	if (mediasize > (off_t)0xffffffff * secsize)
475 		errx(1,
476 		    "disks with more than 2^32-1 sectors are not supported");
477 	(void)lseek(f, (off_t)0, SEEK_SET);
478 	nbytes = read(f, bootarea, BBSIZE);
479 	if (nbytes == -1)
480 		err(4, "%s read", specname);
481 	if (nbytes != BBSIZE)
482 		errx(4, "couldn't read %d bytes from %s", BBSIZE, specname);
483 	close (f);
484 	error = bsd_disklabel_le_dec(
485 	    bootarea + (labeloffset + labelsoffset * secsize),
486 	    &lab, MAXPARTITIONS);
487 	if (flag && error)
488 		errx(1, "%s: no valid label found", specname);
489 
490 	if (is_file)
491 		return(0);
492 
493 	/*
494 	 * Compensate for absolute block addressing by finding the
495 	 * smallest partition offset and if the offset of the 'c'
496 	 * partition is equal to that, subtract it from all offsets.
497 	 */
498 	lba = ~0;
499 	for (i = 0; i < lab.d_npartitions; i++) {
500 		if (lab.d_partitions[i].p_size)
501 			lba = MIN(lba, lab.d_partitions[i].p_offset);
502 	}
503 	if (lba != 0 && lab.d_partitions[RAW_PART].p_offset == lba) {
504 		for (i = 0; i < lab.d_npartitions; i++) {
505 			if (lab.d_partitions[i].p_size)
506 				lab.d_partitions[i].p_offset -= lba;
507 		}
508 		/*
509 		 * Save the offset so that we can write the label
510 		 * back with absolute block addresses.
511 		 */
512 		lba_offset = lba;
513 	}
514 	return (error);
515 }
516 
517 
518 static void
519 display(FILE *f, const struct disklabel *lp)
520 {
521 	int i, j;
522 	const struct partition *pp;
523 
524 	if (lp == NULL)
525 		lp = &lab;
526 
527 	fprintf(f, "# %s:\n", specname);
528 	if (allfields) {
529 		if (lp->d_type < DKMAXTYPES)
530 			fprintf(f, "type: %s\n", dktypenames[lp->d_type]);
531 		else
532 			fprintf(f, "type: %u\n", lp->d_type);
533 		fprintf(f, "disk: %.*s\n", (int)sizeof(lp->d_typename),
534 			lp->d_typename);
535 		fprintf(f, "label: %.*s\n", (int)sizeof(lp->d_packname),
536 			lp->d_packname);
537 		fprintf(f, "flags:");
538 		if (lp->d_flags & D_REMOVABLE)
539 			fprintf(f, " removeable");
540 		if (lp->d_flags & D_ECC)
541 			fprintf(f, " ecc");
542 		if (lp->d_flags & D_BADSECT)
543 			fprintf(f, " badsect");
544 		fprintf(f, "\n");
545 		fprintf(f, "bytes/sector: %lu\n", (u_long)lp->d_secsize);
546 		fprintf(f, "sectors/track: %lu\n", (u_long)lp->d_nsectors);
547 		fprintf(f, "tracks/cylinder: %lu\n", (u_long)lp->d_ntracks);
548 		fprintf(f, "sectors/cylinder: %lu\n", (u_long)lp->d_secpercyl);
549 		fprintf(f, "cylinders: %lu\n", (u_long)lp->d_ncylinders);
550 		fprintf(f, "sectors/unit: %lu\n", (u_long)lp->d_secperunit);
551 		fprintf(f, "rpm: %u\n", lp->d_rpm);
552 		fprintf(f, "interleave: %u\n", lp->d_interleave);
553 		fprintf(f, "trackskew: %u\n", lp->d_trackskew);
554 		fprintf(f, "cylinderskew: %u\n", lp->d_cylskew);
555 		fprintf(f, "headswitch: %lu\t\t# milliseconds\n",
556 		    (u_long)lp->d_headswitch);
557 		fprintf(f, "track-to-track seek: %ld\t# milliseconds\n",
558 		    (u_long)lp->d_trkseek);
559 		fprintf(f, "drivedata: ");
560 		for (i = NDDATA - 1; i >= 0; i--)
561 			if (lp->d_drivedata[i])
562 				break;
563 		if (i < 0)
564 			i = 0;
565 		for (j = 0; j <= i; j++)
566 			fprintf(f, "%lu ", (u_long)lp->d_drivedata[j]);
567 		fprintf(f, "\n\n");
568 	}
569 	fprintf(f, "%u partitions:\n", lp->d_npartitions);
570 	fprintf(f,
571 	    "#          size     offset    fstype   [fsize bsize bps/cpg]\n");
572 	pp = lp->d_partitions;
573 	for (i = 0; i < lp->d_npartitions; i++, pp++) {
574 		if (pp->p_size) {
575 			fprintf(f, "  %c: %10lu %10lu  ", 'a' + i,
576 			   (u_long)pp->p_size, (u_long)pp->p_offset);
577 			if (pp->p_fstype < FSMAXTYPES)
578 				fprintf(f, "%8.8s", fstypenames[pp->p_fstype]);
579 			else
580 				fprintf(f, "%8d", pp->p_fstype);
581 			switch (pp->p_fstype) {
582 
583 			case FS_UNUSED:				/* XXX */
584 				fprintf(f, "    %5lu %5lu %2s",
585 				    (u_long)pp->p_fsize,
586 				    (u_long)(pp->p_fsize * pp->p_frag), "");
587 				break;
588 
589 			case FS_BSDFFS:
590 				fprintf(f, "    %5lu %5lu %5u",
591 				    (u_long)pp->p_fsize,
592 				    (u_long)(pp->p_fsize * pp->p_frag),
593 				    pp->p_cpg);
594 				break;
595 
596 			case FS_BSDLFS:
597 				fprintf(f, "    %5lu %5lu %5d",
598 				    (u_long)pp->p_fsize,
599 				    (u_long)(pp->p_fsize * pp->p_frag),
600 				    pp->p_cpg);
601 				break;
602 
603 			default:
604 				fprintf(f, "%20.20s", "");
605 				break;
606 			}
607 			if (i == RAW_PART) {
608 				fprintf(f, "  # \"raw\" part, don't edit");
609 			}
610 			fprintf(f, "\n");
611 		}
612 	}
613 	fflush(f);
614 }
615 
616 static int
617 edit(void)
618 {
619 	int c, fd;
620 	struct disklabel label;
621 	FILE *fp;
622 
623 	if ((fd = mkstemp(tmpfil)) == -1 ||
624 	    (fp = fdopen(fd, "w")) == NULL) {
625 		warnx("can't create %s", tmpfil);
626 		return (1);
627 	}
628 	display(fp, NULL);
629 	fclose(fp);
630 	for (;;) {
631 		if (!editit())
632 			break;
633 		fp = fopen(tmpfil, "r");
634 		if (fp == NULL) {
635 			warnx("can't reopen %s for reading", tmpfil);
636 			break;
637 		}
638 		bzero((char *)&label, sizeof(label));
639 		c = getasciilabel(fp, &label);
640 		fclose(fp);
641 		if (c) {
642 			lab = label;
643 			if (writelabel() == 0) {
644 				(void) unlink(tmpfil);
645 				return (0);
646 			}
647 		}
648 		printf("re-edit the label? [y]: ");
649 		fflush(stdout);
650 		c = getchar();
651 		if (c != EOF && c != (int)'\n')
652 			while (getchar() != (int)'\n')
653 				;
654 		if  (c == (int)'n')
655 			break;
656 	}
657 	(void) unlink(tmpfil);
658 	return (1);
659 }
660 
661 static int
662 editit(void)
663 {
664 	int pid, xpid;
665 	int locstat, omask;
666 	const char *ed;
667 	uid_t uid;
668 	gid_t gid;
669 
670 	omask = sigblock(sigmask(SIGINT)|sigmask(SIGQUIT)|sigmask(SIGHUP));
671 	while ((pid = fork()) < 0) {
672 		if (errno == EPROCLIM) {
673 			warnx("you have too many processes");
674 			return(0);
675 		}
676 		if (errno != EAGAIN) {
677 			warn("fork");
678 			return(0);
679 		}
680 		sleep(1);
681 	}
682 	if (pid == 0) {
683 		sigsetmask(omask);
684 		gid = getgid();
685 		if (setresgid(gid, gid, gid) == -1)
686 			err(1, "setresgid");
687 		uid = getuid();
688 		if (setresuid(uid, uid, uid) == -1)
689 			err(1, "setresuid");
690 		if ((ed = getenv("EDITOR")) == (char *)0)
691 			ed = DEFEDITOR;
692 		execlp(ed, ed, tmpfil, (char *)0);
693 		err(1, "%s", ed);
694 	}
695 	while ((xpid = wait(&locstat)) >= 0)
696 		if (xpid == pid)
697 			break;
698 	sigsetmask(omask);
699 	return(!locstat);
700 }
701 
702 static char *
703 skip(char *cp)
704 {
705 
706 	while (*cp != '\0' && isspace(*cp))
707 		cp++;
708 	if (*cp == '\0' || *cp == '#')
709 		return (NULL);
710 	return (cp);
711 }
712 
713 static char *
714 word(char *cp)
715 {
716 	char c;
717 
718 	while (*cp != '\0' && !isspace(*cp) && *cp != '#')
719 		cp++;
720 	if ((c = *cp) != '\0') {
721 		*cp++ = '\0';
722 		if (c != '#')
723 			return (skip(cp));
724 	}
725 	return (NULL);
726 }
727 
728 /*
729  * Read an ascii label in from fd f,
730  * in the same format as that put out by display(),
731  * and fill in lp.
732  */
733 static int
734 getasciilabel(FILE *f, struct disklabel *lp)
735 {
736 	char *cp, *endp;
737 	const char **cpp;
738 	u_int part;
739 	char *tp, line[BUFSIZ];
740 	u_long v;
741 	int lineno = 0, errors = 0;
742 	int i;
743 
744 	makelabel("auto", lp);
745 	bzero(&part_set, sizeof(part_set));
746 	bzero(&part_size_type, sizeof(part_size_type));
747 	bzero(&part_offset_type, sizeof(part_offset_type));
748 	lp->d_bbsize = BBSIZE;				/* XXX */
749 	lp->d_sbsize = 0;				/* XXX */
750 	while (fgets(line, sizeof(line) - 1, f)) {
751 		lineno++;
752 		if ((cp = strchr(line,'\n')) != NULL)
753 			*cp = '\0';
754 		cp = skip(line);
755 		if (cp == NULL)
756 			continue;
757 		tp = strchr(cp, ':');
758 		if (tp == NULL) {
759 			fprintf(stderr, "line %d: syntax error\n", lineno);
760 			errors++;
761 			continue;
762 		}
763 		*tp++ = '\0', tp = skip(tp);
764 		if (!strcmp(cp, "type")) {
765 			if (tp == NULL)
766 				tp = unknown;
767 			cpp = dktypenames;
768 			for (; cpp < &dktypenames[DKMAXTYPES]; cpp++)
769 				if (*cpp && !strcmp(*cpp, tp)) {
770 					lp->d_type = cpp - dktypenames;
771 					break;
772 				}
773 			if (cpp < &dktypenames[DKMAXTYPES])
774 				continue;
775 			errno = 0;
776 			v = strtoul(tp, &endp, 10);
777 			if (errno != 0 || *endp != '\0')
778 				v = DKMAXTYPES;
779 			if (v >= DKMAXTYPES)
780 				fprintf(stderr, "line %d:%s %lu\n", lineno,
781 				    "Warning, unknown disk type", v);
782 			else
783 				lp->d_type = v;
784 			continue;
785 		}
786 		if (!strcmp(cp, "flags")) {
787 			for (v = 0; (cp = tp) && *cp != '\0';) {
788 				tp = word(cp);
789 				if (!strcmp(cp, "removeable"))
790 					v |= D_REMOVABLE;
791 				else if (!strcmp(cp, "ecc"))
792 					v |= D_ECC;
793 				else if (!strcmp(cp, "badsect"))
794 					v |= D_BADSECT;
795 				else {
796 					fprintf(stderr,
797 					    "line %d: %s: bad flag\n",
798 					    lineno, cp);
799 					errors++;
800 				}
801 			}
802 			lp->d_flags = v;
803 			continue;
804 		}
805 		if (!strcmp(cp, "drivedata")) {
806 			for (i = 0; (cp = tp) && *cp != '\0' && i < NDDATA;) {
807 				lp->d_drivedata[i++] = strtoul(cp, NULL, 10);
808 				tp = word(cp);
809 			}
810 			continue;
811 		}
812 		if (sscanf(cp, "%lu partitions", &v) == 1) {
813 			if (v > MAXPARTITIONS) {
814 				fprintf(stderr,
815 				    "line %d: bad # of partitions\n", lineno);
816 				lp->d_npartitions = MAXPARTITIONS;
817 				errors++;
818 			} else if (v < DEFPARTITIONS) {
819 				fprintf(stderr,
820 				    "line %d: bad # of partitions\n", lineno);
821 				lp->d_npartitions = DEFPARTITIONS;
822 				errors++;
823 			} else
824 				lp->d_npartitions = v;
825 			continue;
826 		}
827 		if (tp == NULL)
828 			tp = blank;
829 		if (!strcmp(cp, "disk")) {
830 			strncpy(lp->d_typename, tp, sizeof (lp->d_typename));
831 			continue;
832 		}
833 		if (!strcmp(cp, "label")) {
834 			strncpy(lp->d_packname, tp, sizeof (lp->d_packname));
835 			continue;
836 		}
837 		if (!strcmp(cp, "bytes/sector")) {
838 			v = strtoul(tp, NULL, 10);
839 			if (v == 0 || (v % DEV_BSIZE) != 0) {
840 				fprintf(stderr,
841 				    "line %d: %s: bad sector size\n",
842 				    lineno, tp);
843 				errors++;
844 			} else
845 				lp->d_secsize = v;
846 			continue;
847 		}
848 		if (!strcmp(cp, "sectors/track")) {
849 			v = strtoul(tp, NULL, 10);
850 #if (ULONG_MAX != 0xffffffffUL)
851 			if (v == 0 || v > 0xffffffff)
852 #else
853 			if (v == 0)
854 #endif
855 			{
856 				fprintf(stderr, "line %d: %s: bad %s\n",
857 				    lineno, tp, cp);
858 				errors++;
859 			} else
860 				lp->d_nsectors = v;
861 			continue;
862 		}
863 		if (!strcmp(cp, "sectors/cylinder")) {
864 			v = strtoul(tp, NULL, 10);
865 			if (v == 0) {
866 				fprintf(stderr, "line %d: %s: bad %s\n",
867 				    lineno, tp, cp);
868 				errors++;
869 			} else
870 				lp->d_secpercyl = v;
871 			continue;
872 		}
873 		if (!strcmp(cp, "tracks/cylinder")) {
874 			v = strtoul(tp, NULL, 10);
875 			if (v == 0) {
876 				fprintf(stderr, "line %d: %s: bad %s\n",
877 				    lineno, tp, cp);
878 				errors++;
879 			} else
880 				lp->d_ntracks = v;
881 			continue;
882 		}
883 		if (!strcmp(cp, "cylinders")) {
884 			v = strtoul(tp, NULL, 10);
885 			if (v == 0) {
886 				fprintf(stderr, "line %d: %s: bad %s\n",
887 				    lineno, tp, cp);
888 				errors++;
889 			} else
890 				lp->d_ncylinders = v;
891 			continue;
892 		}
893 		if (!strcmp(cp, "sectors/unit")) {
894 			v = strtoul(tp, NULL, 10);
895 			if (v == 0) {
896 				fprintf(stderr, "line %d: %s: bad %s\n",
897 				    lineno, tp, cp);
898 				errors++;
899 			} else
900 				lp->d_secperunit = v;
901 			continue;
902 		}
903 		if (!strcmp(cp, "rpm")) {
904 			v = strtoul(tp, NULL, 10);
905 			if (v == 0 || v > USHRT_MAX) {
906 				fprintf(stderr, "line %d: %s: bad %s\n",
907 				    lineno, tp, cp);
908 				errors++;
909 			} else
910 				lp->d_rpm = v;
911 			continue;
912 		}
913 		if (!strcmp(cp, "interleave")) {
914 			v = strtoul(tp, NULL, 10);
915 			if (v == 0 || v > USHRT_MAX) {
916 				fprintf(stderr, "line %d: %s: bad %s\n",
917 				    lineno, tp, cp);
918 				errors++;
919 			} else
920 				lp->d_interleave = v;
921 			continue;
922 		}
923 		if (!strcmp(cp, "trackskew")) {
924 			v = strtoul(tp, NULL, 10);
925 			if (v > USHRT_MAX) {
926 				fprintf(stderr, "line %d: %s: bad %s\n",
927 				    lineno, tp, cp);
928 				errors++;
929 			} else
930 				lp->d_trackskew = v;
931 			continue;
932 		}
933 		if (!strcmp(cp, "cylinderskew")) {
934 			v = strtoul(tp, NULL, 10);
935 			if (v > USHRT_MAX) {
936 				fprintf(stderr, "line %d: %s: bad %s\n",
937 				    lineno, tp, cp);
938 				errors++;
939 			} else
940 				lp->d_cylskew = v;
941 			continue;
942 		}
943 		if (!strcmp(cp, "headswitch")) {
944 			v = strtoul(tp, NULL, 10);
945 			lp->d_headswitch = v;
946 			continue;
947 		}
948 		if (!strcmp(cp, "track-to-track seek")) {
949 			v = strtoul(tp, NULL, 10);
950 			lp->d_trkseek = v;
951 			continue;
952 		}
953 		/* the ':' was removed above */
954 		if (*cp < 'a' || *cp > MAX_PART || cp[1] != '\0') {
955 			fprintf(stderr,
956 			    "line %d: %s: Unknown disklabel field\n", lineno,
957 			    cp);
958 			errors++;
959 			continue;
960 		}
961 
962 		/* Process a partition specification line. */
963 		part = *cp - 'a';
964 		if (part >= lp->d_npartitions) {
965 			fprintf(stderr,
966 			    "line %d: partition name out of range a-%c: %s\n",
967 			    lineno, 'a' + lp->d_npartitions - 1, cp);
968 			errors++;
969 			continue;
970 		}
971 		part_set[part] = 1;
972 
973 		if (getasciipartspec(tp, lp, part, lineno) != 0) {
974 			errors++;
975 			break;
976 		}
977 	}
978 	errors += checklabel(lp);
979 	return (errors == 0);
980 }
981 
982 #define NXTNUM(n) do { \
983 	if (tp == NULL) { \
984 		fprintf(stderr, "line %d: too few numeric fields\n", lineno); \
985 		return (1); \
986 	} else { \
987 		cp = tp, tp = word(cp); \
988 		(n) = strtoul(cp, NULL, 10); \
989 	} \
990 } while (0)
991 
992 /* retain 1 character following number */
993 #define NXTWORD(w,n) do { \
994 	if (tp == NULL) { \
995 		fprintf(stderr, "line %d: too few numeric fields\n", lineno); \
996 		return (1); \
997 	} else { \
998 		char *tmp; \
999 		cp = tp, tp = word(cp); \
1000 		(n) = strtoul(cp, &tmp, 10); \
1001 		if (tmp) (w) = *tmp; \
1002 	} \
1003 } while (0)
1004 
1005 /*
1006  * Read a partition line into partition `part' in the specified disklabel.
1007  * Return 0 on success, 1 on failure.
1008  */
1009 static int
1010 getasciipartspec(char *tp, struct disklabel *lp, int part, int lineno)
1011 {
1012 	struct partition *pp;
1013 	char *cp, *endp;
1014 	const char **cpp;
1015 	u_long v;
1016 
1017 	pp = &lp->d_partitions[part];
1018 	cp = NULL;
1019 
1020 	v = 0;
1021 	NXTWORD(part_size_type[part],v);
1022 	if (v == 0 && part_size_type[part] != '*') {
1023 		fprintf(stderr,
1024 		    "line %d: %s: bad partition size\n", lineno, cp);
1025 		return (1);
1026 	}
1027 	pp->p_size = v;
1028 
1029 	v = 0;
1030 	NXTWORD(part_offset_type[part],v);
1031 	if (v == 0 && part_offset_type[part] != '*' &&
1032 	    part_offset_type[part] != '\0') {
1033 		fprintf(stderr,
1034 		    "line %d: %s: bad partition offset\n", lineno, cp);
1035 		return (1);
1036 	}
1037 	pp->p_offset = v;
1038 	if (tp == NULL) {
1039 		fprintf(stderr, "line %d: missing file system type\n", lineno);
1040 		return (1);
1041 	}
1042 	cp = tp, tp = word(cp);
1043 	for (cpp = fstypenames; cpp < &fstypenames[FSMAXTYPES]; cpp++)
1044 		if (*cpp && !strcmp(*cpp, cp))
1045 			break;
1046 	if (*cpp != NULL) {
1047 		pp->p_fstype = cpp - fstypenames;
1048 	} else {
1049 		if (isdigit(*cp)) {
1050 			errno = 0;
1051 			v = strtoul(cp, &endp, 10);
1052 			if (errno != 0 || *endp != '\0')
1053 				v = FSMAXTYPES;
1054 		} else
1055 			v = FSMAXTYPES;
1056 		if (v >= FSMAXTYPES) {
1057 			fprintf(stderr,
1058 			    "line %d: Warning, unknown file system type %s\n",
1059 			    lineno, cp);
1060 			v = FS_UNUSED;
1061 		}
1062 		pp->p_fstype = v;
1063 	}
1064 
1065 	switch (pp->p_fstype) {
1066 	case FS_UNUSED:
1067 	case FS_BSDFFS:
1068 	case FS_BSDLFS:
1069 		/* accept defaults for fsize/frag/cpg */
1070 		if (tp) {
1071 			NXTNUM(pp->p_fsize);
1072 			if (pp->p_fsize == 0)
1073 				break;
1074 			NXTNUM(v);
1075 			pp->p_frag = v / pp->p_fsize;
1076 			if (tp != NULL)
1077 				NXTNUM(pp->p_cpg);
1078 		}
1079 		/* else default to 0's */
1080 		break;
1081 	default:
1082 		break;
1083 	}
1084 	return (0);
1085 }
1086 
1087 /*
1088  * Check disklabel for errors and fill in
1089  * derived fields according to supplied values.
1090  */
1091 static int
1092 checklabel(struct disklabel *lp)
1093 {
1094 	struct partition *pp;
1095 	int i, errors = 0;
1096 	char part;
1097 	u_long base_offset, needed, total_size, total_percent, current_offset;
1098 	long free_space;
1099 	int seen_default_offset;
1100 	int hog_part;
1101 	int j;
1102 	struct partition *pp2;
1103 
1104 	if (lp == NULL)
1105 		lp = &lab;
1106 
1107 	if (allfields) {
1108 
1109 		if (lp->d_secsize == 0) {
1110 			fprintf(stderr, "sector size 0\n");
1111 			return (1);
1112 		}
1113 		if (lp->d_nsectors == 0) {
1114 			fprintf(stderr, "sectors/track 0\n");
1115 			return (1);
1116 		}
1117 		if (lp->d_ntracks == 0) {
1118 			fprintf(stderr, "tracks/cylinder 0\n");
1119 			return (1);
1120 		}
1121 		if  (lp->d_ncylinders == 0) {
1122 			fprintf(stderr, "cylinders/unit 0\n");
1123 			errors++;
1124 		}
1125 		if (lp->d_rpm == 0)
1126 			warnx("revolutions/minute 0");
1127 		if (lp->d_secpercyl == 0)
1128 			lp->d_secpercyl = lp->d_nsectors * lp->d_ntracks;
1129 		if (lp->d_secperunit == 0)
1130 			lp->d_secperunit = lp->d_secpercyl * lp->d_ncylinders;
1131 		if (lp->d_bbsize == 0) {
1132 			fprintf(stderr, "boot block size 0\n");
1133 			errors++;
1134 		} else if (lp->d_bbsize % lp->d_secsize)
1135 			warnx("boot block size %% sector-size != 0");
1136 		if (lp->d_npartitions > MAXPARTITIONS) {
1137 			warnx("number of partitions (%lu) > MAXPARTITIONS (%d)",
1138 			    (u_long)lp->d_npartitions, MAXPARTITIONS);
1139 			errors++;
1140 		}
1141 		if (lp->d_npartitions < DEFPARTITIONS) {
1142 			warnx("number of partitions (%lu) < DEFPARTITIONS (%d)",
1143 			    (u_long)lp->d_npartitions, DEFPARTITIONS);
1144 			errors++;
1145 		}
1146 	} else {
1147 		struct disklabel *vl;
1148 
1149 		vl = getvirginlabel();
1150 		if (lp->d_secsize == 0)
1151 			lp->d_secsize = vl->d_secsize;
1152 		if (lp->d_nsectors == 0)
1153 			lp->d_nsectors = vl->d_nsectors;
1154 		if (lp->d_ntracks == 0)
1155 			lp->d_ntracks = vl->d_ntracks;
1156 		if (lp->d_ncylinders == 0)
1157 			lp->d_ncylinders = vl->d_ncylinders;
1158 		if (lp->d_rpm == 0)
1159 			lp->d_rpm = vl->d_rpm;
1160 		if (lp->d_interleave == 0)
1161 			lp->d_interleave = vl->d_interleave;
1162 		if (lp->d_secpercyl == 0)
1163 			lp->d_secpercyl = vl->d_secpercyl;
1164 		if (lp->d_secperunit == 0 ||
1165 		    lp->d_secperunit > vl->d_secperunit)
1166 			lp->d_secperunit = vl->d_secperunit;
1167 		if (lp->d_bbsize == 0)
1168 			lp->d_bbsize = vl->d_bbsize;
1169 		if (lp->d_npartitions < DEFPARTITIONS ||
1170 		    lp->d_npartitions > MAXPARTITIONS)
1171 			lp->d_npartitions = vl->d_npartitions;
1172 	}
1173 
1174 
1175 	/* first allocate space to the partitions, then offsets */
1176 	total_size = 0; /* in sectors */
1177 	total_percent = 0; /* in percent */
1178 	hog_part = -1;
1179 	/* find all fixed partitions */
1180 	for (i = 0; i < lp->d_npartitions; i++) {
1181 		pp = &lp->d_partitions[i];
1182 		if (part_set[i]) {
1183 			if (part_size_type[i] == '*') {
1184 				if (i == RAW_PART) {
1185 					pp->p_size = lp->d_secperunit;
1186 				} else {
1187 					if (hog_part != -1)
1188 						warnx("Too many '*' partitions (%c and %c)",
1189 						    hog_part + 'a',i + 'a');
1190 					else
1191 						hog_part = i;
1192 				}
1193 			} else {
1194 				off_t size;
1195 
1196 				size = pp->p_size;
1197 				switch (part_size_type[i]) {
1198 				case '%':
1199 					total_percent += size;
1200 					break;
1201 				case 't':
1202 				case 'T':
1203 					size *= 1024ULL;
1204 					/* FALLTHROUGH */
1205 				case 'g':
1206 				case 'G':
1207 					size *= 1024ULL;
1208 					/* FALLTHROUGH */
1209 				case 'm':
1210 				case 'M':
1211 					size *= 1024ULL;
1212 					/* FALLTHROUGH */
1213 				case 'k':
1214 				case 'K':
1215 					size *= 1024ULL;
1216 					break;
1217 				case '\0':
1218 					break;
1219 				default:
1220 					warnx("unknown multiplier suffix '%c' for partition %c (should be K, M, G or T)",
1221 					    part_size_type[i], i + 'a');
1222 					break;
1223 				}
1224 				/* don't count %'s yet */
1225 				if (part_size_type[i] != '%') {
1226 					/*
1227 					 * for all not in sectors, convert to
1228 					 * sectors
1229 					 */
1230 					if (part_size_type[i] != '\0') {
1231 						if (size % lp->d_secsize != 0)
1232 							warnx("partition %c not an integer number of sectors",
1233 							    i + 'a');
1234 						size /= lp->d_secsize;
1235 						pp->p_size = size;
1236 					}
1237 					/* else already in sectors */
1238 					if (i != RAW_PART)
1239 						total_size += size;
1240 				}
1241 			}
1242 		}
1243 	}
1244 
1245 	/* Find out the total free space, excluding the boot block area. */
1246 	base_offset = BBSIZE / secsize;
1247 	free_space = 0;
1248 	for (i = 0; i < lp->d_npartitions; i++) {
1249 		pp = &lp->d_partitions[i];
1250 		if (!part_set[i] || i == RAW_PART ||
1251 		    part_size_type[i] == '%' || part_size_type[i] == '*')
1252 			continue;
1253 		if (pp->p_offset > base_offset)
1254 			free_space += pp->p_offset - base_offset;
1255 		if (pp->p_offset + pp->p_size > base_offset)
1256 			base_offset = pp->p_offset + pp->p_size;
1257 	}
1258 	if (base_offset < lp->d_secperunit)
1259 		free_space += lp->d_secperunit - base_offset;
1260 
1261 	/* handle % partitions - note %'s don't need to add up to 100! */
1262 	if (total_percent != 0) {
1263 		if (total_percent > 100) {
1264 			fprintf(stderr,"total percentage %lu is greater than 100\n",
1265 			    total_percent);
1266 			errors++;
1267 		}
1268 
1269 		if (free_space > 0) {
1270 			for (i = 0; i < lp->d_npartitions; i++) {
1271 				pp = &lp->d_partitions[i];
1272 				if (part_set[i] && part_size_type[i] == '%') {
1273 					/* careful of overflows! and integer roundoff */
1274 					pp->p_size = ((double)pp->p_size/100) * free_space;
1275 					total_size += pp->p_size;
1276 
1277 					/* FIX we can lose a sector or so due to roundoff per
1278 					   partition.  A more complex algorithm could avoid that */
1279 				}
1280 			}
1281 		} else {
1282 			fprintf(stderr,
1283 			    "%ld sectors available to give to '*' and '%%' partitions\n",
1284 			    free_space);
1285 			errors++;
1286 			/* fix?  set all % partitions to size 0? */
1287 		}
1288 	}
1289 	/* give anything remaining to the hog partition */
1290 	if (hog_part != -1) {
1291 		/*
1292 		 * Find the range of offsets usable by '*' partitions around
1293 		 * the hog partition and how much space they need.
1294 		 */
1295 		needed = 0;
1296 		base_offset = BBSIZE / secsize;
1297 		for (i = hog_part - 1; i >= 0; i--) {
1298 			pp = &lp->d_partitions[i];
1299 			if (!part_set[i] || i == RAW_PART)
1300 				continue;
1301 			if (part_offset_type[i] == '*') {
1302 				needed += pp->p_size;
1303 				continue;
1304 			}
1305 			base_offset = pp->p_offset + pp->p_size;
1306 			break;
1307 		}
1308 		current_offset = lp->d_secperunit;
1309 		for (i = lp->d_npartitions - 1; i > hog_part; i--) {
1310 			pp = &lp->d_partitions[i];
1311 			if (!part_set[i] || i == RAW_PART)
1312 				continue;
1313 			if (part_offset_type[i] == '*') {
1314 				needed += pp->p_size;
1315 				continue;
1316 			}
1317 			current_offset = pp->p_offset;
1318 		}
1319 
1320 		if (current_offset - base_offset <= needed) {
1321 			fprintf(stderr, "Cannot find space for partition %c\n",
1322 			    hog_part + 'a');
1323 			fprintf(stderr,
1324 			    "Need more than %lu sectors between %lu and %lu\n",
1325 			    needed, base_offset, current_offset);
1326 			errors++;
1327 			lp->d_partitions[hog_part].p_size = 0;
1328 		} else {
1329 			lp->d_partitions[hog_part].p_size = current_offset -
1330 			    base_offset - needed;
1331 			total_size += lp->d_partitions[hog_part].p_size;
1332 		}
1333 	}
1334 
1335 	/* Now set the offsets for each partition */
1336 	current_offset = BBSIZE / secsize; /* in sectors */
1337 	seen_default_offset = 0;
1338 	for (i = 0; i < lp->d_npartitions; i++) {
1339 		part = 'a' + i;
1340 		pp = &lp->d_partitions[i];
1341 		if (part_set[i]) {
1342 			if (part_offset_type[i] == '*') {
1343 				if (i == RAW_PART) {
1344 					pp->p_offset = 0;
1345 				} else {
1346 					pp->p_offset = current_offset;
1347 					seen_default_offset = 1;
1348 				}
1349 			} else {
1350 				/* allow them to be out of order for old-style tables */
1351 				if (pp->p_offset < current_offset &&
1352 				    seen_default_offset && i != RAW_PART &&
1353 				    pp->p_fstype != FS_VINUM) {
1354 					fprintf(stderr,
1355 "Offset %ld for partition %c overlaps previous partition which ends at %lu\n",
1356 					    (long)pp->p_offset,i+'a',current_offset);
1357 					fprintf(stderr,
1358 "Labels with any *'s for offset must be in ascending order by sector\n");
1359 					errors++;
1360 				} else if (pp->p_offset != current_offset &&
1361 				    i != RAW_PART && seen_default_offset) {
1362 					/*
1363 					 * this may give unneeded warnings if
1364 					 * partitions are out-of-order
1365 					 */
1366 					warnx(
1367 "Offset %ld for partition %c doesn't match expected value %ld",
1368 					    (long)pp->p_offset, i + 'a', current_offset);
1369 				}
1370 			}
1371 			if (i != RAW_PART)
1372 				current_offset = pp->p_offset + pp->p_size;
1373 		}
1374 	}
1375 
1376 	for (i = 0; i < lp->d_npartitions; i++) {
1377 		part = 'a' + i;
1378 		pp = &lp->d_partitions[i];
1379 		if (pp->p_size == 0 && pp->p_offset != 0)
1380 			warnx("partition %c: size 0, but offset %lu",
1381 			    part, (u_long)pp->p_offset);
1382 #ifdef notdef
1383 		if (pp->p_size % lp->d_secpercyl)
1384 			warnx("partition %c: size %% cylinder-size != 0",
1385 			    part);
1386 		if (pp->p_offset % lp->d_secpercyl)
1387 			warnx("partition %c: offset %% cylinder-size != 0",
1388 			    part);
1389 #endif
1390 		if (pp->p_offset > lp->d_secperunit) {
1391 			fprintf(stderr,
1392 			    "partition %c: offset past end of unit\n", part);
1393 			errors++;
1394 		}
1395 		if (pp->p_offset + pp->p_size > lp->d_secperunit) {
1396 			fprintf(stderr,
1397 			"partition %c: partition extends past end of unit\n",
1398 			    part);
1399 			errors++;
1400 		}
1401 		if (i == RAW_PART) {
1402 			if (pp->p_fstype != FS_UNUSED)
1403 				warnx("partition %c is not marked as unused!",part);
1404 			if (pp->p_offset != 0)
1405 				warnx("partition %c doesn't start at 0!",part);
1406 			if (pp->p_size != lp->d_secperunit)
1407 				warnx("partition %c doesn't cover the whole unit!",part);
1408 
1409 			if ((pp->p_fstype != FS_UNUSED) || (pp->p_offset != 0) ||
1410 			    (pp->p_size != lp->d_secperunit)) {
1411 				warnx("An incorrect partition %c may cause problems for "
1412 				    "standard system utilities",part);
1413 			}
1414 		}
1415 
1416 		/* check for overlaps */
1417 		/* this will check for all possible overlaps once and only once */
1418 		for (j = 0; j < i; j++) {
1419 			pp2 = &lp->d_partitions[j];
1420 			if (j != RAW_PART && i != RAW_PART &&
1421 			    pp->p_fstype != FS_VINUM &&
1422 			    pp2->p_fstype != FS_VINUM &&
1423 			    part_set[i] && part_set[j]) {
1424 				if (pp2->p_offset < pp->p_offset + pp->p_size &&
1425 				    (pp2->p_offset + pp2->p_size > pp->p_offset ||
1426 					pp2->p_offset >= pp->p_offset)) {
1427 					fprintf(stderr,"partitions %c and %c overlap!\n",
1428 					    j + 'a', i + 'a');
1429 					errors++;
1430 				}
1431 			}
1432 		}
1433 	}
1434 	for (; i < lp->d_npartitions; i++) {
1435 		part = 'a' + i;
1436 		pp = &lp->d_partitions[i];
1437 		if (pp->p_size || pp->p_offset)
1438 			warnx("unused partition %c: size %d offset %lu",
1439 			    'a' + i, pp->p_size, (u_long)pp->p_offset);
1440 	}
1441 	return (errors);
1442 }
1443 
1444 /*
1445  * When operating on a "virgin" disk, try getting an initial label
1446  * from the associated device driver.  This might work for all device
1447  * drivers that are able to fetch some initial device parameters
1448  * without even having access to a (BSD) disklabel, like SCSI disks,
1449  * most IDE drives, or vn devices.
1450  *
1451  * The device name must be given in its "canonical" form.
1452  */
1453 static struct disklabel *
1454 getvirginlabel(void)
1455 {
1456 	static struct disklabel loclab;
1457 	struct partition *dp;
1458 	int f;
1459 	u_int u;
1460 
1461 	if ((f = open(specname, O_RDONLY)) == -1) {
1462 		warn("cannot open %s", specname);
1463 		return (NULL);
1464 	}
1465 
1466 	if (is_file)
1467 		get_file_parms(f);
1468 	else {
1469 		mediasize = g_mediasize(f);
1470 		secsize = g_sectorsize(f);
1471 		if (secsize < 0 || mediasize < 0) {
1472 			close (f);
1473 			return (NULL);
1474 		}
1475 	}
1476 	memset(&loclab, 0, sizeof loclab);
1477 	loclab.d_magic = DISKMAGIC;
1478 	loclab.d_magic2 = DISKMAGIC;
1479 	loclab.d_secsize = secsize;
1480 	loclab.d_secperunit = mediasize / secsize;
1481 
1482 	/*
1483 	 * Nobody in these enlightened days uses the CHS geometry for
1484 	 * anything, but nonetheless try to get it right.  If we fail
1485 	 * to get any good ideas from the device, construct something
1486 	 * which is IBM-PC friendly.
1487 	 */
1488 	if (ioctl(f, DIOCGFWSECTORS, &u) == 0)
1489 		loclab.d_nsectors = u;
1490 	else
1491 		loclab.d_nsectors = 63;
1492 	if (ioctl(f, DIOCGFWHEADS, &u) == 0)
1493 		loclab.d_ntracks = u;
1494 	else if (loclab.d_secperunit <= 63*1*1024)
1495 		loclab.d_ntracks = 1;
1496 	else if (loclab.d_secperunit <= 63*16*1024)
1497 		loclab.d_ntracks = 16;
1498 	else
1499 		loclab.d_ntracks = 255;
1500 	loclab.d_secpercyl = loclab.d_ntracks * loclab.d_nsectors;
1501 	loclab.d_ncylinders = loclab.d_secperunit / loclab.d_secpercyl;
1502 	loclab.d_npartitions = DEFPARTITIONS;
1503 
1504 	/* Various (unneeded) compat stuff */
1505 	loclab.d_rpm = 3600;
1506 	loclab.d_bbsize = BBSIZE;
1507 	loclab.d_interleave = 1;
1508 	strncpy(loclab.d_typename, "amnesiac",
1509 	    sizeof(loclab.d_typename));
1510 
1511 	dp = &loclab.d_partitions[RAW_PART];
1512 	dp->p_size = loclab.d_secperunit;
1513 	loclab.d_checksum = dkcksum(&loclab);
1514 	close (f);
1515 	return (&loclab);
1516 }
1517 
1518 static void
1519 usage(void)
1520 {
1521 
1522 	fprintf(stderr,
1523 	"%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",
1524 	"usage: bsdlabel disk",
1525 	"\t\t(to read label)",
1526 	"	bsdlabel -w [-n] [-m machine] disk [type]",
1527 	"\t\t(to write label with existing boot program)",
1528 	"	bsdlabel -e [-n] [-m machine] disk",
1529 	"\t\t(to edit label)",
1530 	"	bsdlabel -R [-n] [-m machine] disk protofile",
1531 	"\t\t(to restore label with existing boot program)",
1532 	"	bsdlabel -B [-b boot] [-m machine] disk",
1533 	"\t\t(to install boot program with existing on-disk label)",
1534 	"	bsdlabel -w -B [-n] [-b boot] [-m machine] disk [type]",
1535 	"\t\t(to write label and install boot program)",
1536 	"	bsdlabel -R -B [-n] [-b boot] [-m machine] disk protofile",
1537 		"\t\t(to restore label and install boot program)"
1538 	);
1539 	exit(1);
1540 }
1541