xref: /openbsd-src/sbin/disklabel/disklabel.c (revision b2ea75c1b17e1a9a339660e7ed45cd24946b230e)
1 /*	$OpenBSD: disklabel.c,v 1.70 2001/07/07 18:26:10 deraadt Exp $	*/
2 
3 /*
4  * Copyright (c) 1987, 1993
5  *	The Regents of the University of California.  All rights reserved.
6  *
7  * This code is derived from software contributed to Berkeley by
8  * Symmetric Computer Systems.
9  *
10  * Redistribution and use in source and binary forms, with or without
11  * modification, are permitted provided that the following conditions
12  * are met:
13  * 1. Redistributions of source code must retain the above copyright
14  *    notice, this list of conditions and the following disclaimer.
15  * 2. Redistributions in binary form must reproduce the above copyright
16  *    notice, this list of conditions and the following disclaimer in the
17  *    documentation and/or other materials provided with the distribution.
18  * 3. All advertising materials mentioning features or use of this software
19  *    must display the following acknowledgement:
20  *	This product includes software developed by the University of
21  *	California, Berkeley and its contributors.
22  * 4. Neither the name of the University nor the names of its contributors
23  *    may be used to endorse or promote products derived from this software
24  *    without specific prior written permission.
25  *
26  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
27  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
28  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
29  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
30  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
31  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
32  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
33  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
34  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
35  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
36  * SUCH DAMAGE.
37  */
38 
39 #ifndef lint
40 static char copyright[] =
41 "@(#) Copyright (c) 1987, 1993\n\
42 	The Regents of the University of California.  All rights reserved.\n";
43 #endif /* not lint */
44 
45 #ifndef lint
46 static char rcsid[] = "$OpenBSD: disklabel.c,v 1.70 2001/07/07 18:26:10 deraadt Exp $";
47 #endif /* not lint */
48 
49 #include <sys/param.h>
50 #include <sys/ioctl.h>
51 #include <sys/stat.h>
52 #include <sys/wait.h>
53 #define DKTYPENAMES
54 #include <sys/disklabel.h>
55 
56 #include <ufs/ffs/fs.h>
57 
58 #include <ctype.h>
59 #include <err.h>
60 #include <errno.h>
61 #include <fcntl.h>
62 #include <signal.h>
63 #include <string.h>
64 #include <stdio.h>
65 #include <stdlib.h>
66 #include <unistd.h>
67 #include <util.h>
68 #include "pathnames.h"
69 
70 /*
71  * Disklabel: read and write disklabels.
72  * The label is usually placed on one of the first sectors of the disk.
73  * Many machines also place a bootstrap in the same area,
74  * in which case the label is embedded in the bootstrap.
75  * The bootstrap source must leave space at the proper offset
76  * for the label on such machines.
77  */
78 
79 #ifndef BBSIZE
80 #define	BBSIZE	8192			/* size of boot area, with label */
81 #endif
82 
83 #ifndef NUMBOOT
84 #define NUMBOOT 0
85 #endif
86 
87 char	*dkname, *specname;
88 char	tmpfil[] = _PATH_TMPFILE;
89 char	namebuf[BBSIZE], *np = namebuf;
90 struct	disklabel lab;
91 char	bootarea[BBSIZE];
92 
93 #if NUMBOOT > 0
94 int	installboot;	/* non-zero if we should install a boot program */
95 char	*bootbuf;	/* pointer to buffer with remainder of boot prog */
96 int	bootsize;	/* size of remaining boot program */
97 char	*xxboot;	/* primary boot */
98 char	*bootxx;	/* secondary boot */
99 char	boot0[MAXPATHLEN];
100 #if NUMBOOT > 1
101 char	boot1[MAXPATHLEN];
102 #endif
103 #endif
104 
105 enum {
106 	UNSPEC, EDIT, EDITOR, READ, RESTORE, SETWRITEABLE, WRITE, WRITEBOOT
107 } op = UNSPEC;
108 
109 int	cflag;
110 int	dflag;
111 int	rflag;
112 int	tflag;
113 int	nwflag;
114 int	verbose;
115 int	donothing;
116 
117 #ifdef DOSLABEL
118 struct dos_partition *dosdp;	/* DOS partition, if found */
119 struct dos_partition *readmbr __P((int));
120 #endif
121 
122 void	makedisktab __P((FILE *, struct disklabel *));
123 void	makelabel __P((char *, char *, struct disklabel *));
124 int	writelabel __P((int, char *, struct disklabel *));
125 void	l_perror __P((char *));
126 struct disklabel *readlabel __P((int));
127 struct disklabel *makebootarea __P((char *, struct disklabel *, int));
128 void	display __P((FILE *, struct disklabel *));
129 void	display_partition __P((FILE *, struct disklabel *, char **, int, char, int));
130 int	width_partition __P((struct disklabel *, int));
131 int	editor __P((struct disklabel *, int, char *, char *));
132 int	edit __P((struct disklabel *, int));
133 int	editit __P((void));
134 char	*skip __P((char *));
135 char	*word __P((char *));
136 int	getasciilabel __P((FILE *, struct disklabel *));
137 int	checklabel __P((struct disklabel *));
138 int	cmplabel __P((struct disklabel *, struct disklabel *));
139 void	setbootflag __P((struct disklabel *));
140 void	usage __P((void));
141 u_short	dkcksum __P((struct disklabel *));
142 
143 int
144 main(argc, argv)
145 	int argc;
146 	char *argv[];
147 {
148 	int ch, f, writeable, error = 0;
149 	char *fstabfile = NULL;
150 	struct disklabel *lp;
151 	FILE *t;
152 
153 	while ((ch = getopt(argc, argv, "BEFf:NRWb:cdenrs:tvw")) != -1)
154 		switch (ch) {
155 #if NUMBOOT > 0
156 		case 'B':
157 			++installboot;
158 			break;
159 		case 'b':
160 			xxboot = optarg;
161 			break;
162 #if NUMBOOT > 1
163 		case 's':
164 			bootxx = optarg;
165 			break;
166 #endif
167 #endif
168 		case 'N':
169 			if (op != UNSPEC)
170 				usage();
171 			writeable = 0;
172 			op = SETWRITEABLE;
173 			break;
174 		case 'R':
175 			if (op != UNSPEC)
176 				usage();
177 			op = RESTORE;
178 			break;
179 		case 'W':
180 			if (op != UNSPEC)
181 				usage();
182 			writeable = 1;
183 			op = SETWRITEABLE;
184 			break;
185 		case 'c':
186 			++cflag;
187 			break;
188 		case 'd':
189 			++dflag;
190 			break;
191 		case 'e':
192 			if (op != UNSPEC)
193 				usage();
194 			op = EDIT;
195 			break;
196 		case 'E':
197 			if (op != UNSPEC)
198 				usage();
199 			op = EDITOR;
200 			break;
201 		case 'f':
202 			fstabfile = optarg;
203 			break;
204 		case 'r':
205 			++rflag;
206 			break;
207 		case 't':
208 			++tflag;
209 			break;
210 		case 'w':
211 			if (op != UNSPEC)
212 				usage();
213 			op = WRITE;
214 			break;
215 		case 'n':
216 			donothing++;
217 			break;
218 		case 'v':
219 			verbose++;
220 			break;
221 		case '?':
222 		default:
223 			usage();
224 	}
225 	argc -= optind;
226 	argv += optind;
227 
228 #if NUMBOOT > 0
229 	if (installboot) {
230 		rflag++;
231 		if (op == UNSPEC)
232 			op = WRITEBOOT;
233 	} else {
234 		if (op == UNSPEC)
235 			op = READ;
236 	}
237 #else
238 	if (op == UNSPEC)
239 		op = READ;
240 #endif
241 
242 	if (argc < 1 || (rflag && cflag + dflag > 0) ||
243 	    (fstabfile && op != EDITOR))
244 		usage();
245 
246 	dkname = argv[0];
247 	f = opendev(dkname, (op == READ ? O_RDONLY : O_RDWR), OPENDEV_PART,
248 	    &specname);
249 	if (f < 0)
250 		err(4, "%s", specname);
251 
252 #ifdef DOSLABEL
253 	/*
254 	 * Check for presence of DOS partition table in
255 	 * master boot record. Return pointer to OpenBSD
256 	 * partition, if present. If no valid partition table,
257 	 * return 0. If valid partition table present, but no
258 	 * partition to use, return a pointer to a non-386bsd
259 	 * partition.
260 	 */
261 	dosdp = readmbr(f);
262 #endif
263 
264 	switch (op) {
265 	case EDIT:
266 		if (argc != 1)
267 			usage();
268 		if ((lp = readlabel(f)) == NULL)
269 			exit(1);
270 		error = edit(lp, f);
271 		break;
272 	case EDITOR:
273 		if (argc != 1)
274 			usage();
275 		if ((lp = readlabel(f)) == NULL)
276 			exit(1);
277 		error = editor(lp, f, specname, fstabfile);
278 		break;
279 	case READ:
280 		if (argc != 1)
281 			usage();
282 		if ((lp = readlabel(f)) == NULL)
283 			exit(1);
284 		if (tflag)
285 			makedisktab(stdout, lp);
286 		else
287 			display(stdout, lp);
288 		error = checklabel(lp);
289 		break;
290 	case RESTORE:
291 		if (argc < 2 || argc > 3)
292 			usage();
293 #if NUMBOOT > 0
294 		if (installboot && argc == 3)
295 			makelabel(argv[2], NULL, &lab);
296 #endif
297 		lp = makebootarea(bootarea, &lab, f);
298 		if (!(t = fopen(argv[1], "r")))
299 			err(4, "%s", argv[1]);
300 		if (getasciilabel(t, lp))
301 			error = writelabel(f, bootarea, lp);
302 		break;
303 	case SETWRITEABLE:
304 		if (!donothing) {
305 			if (ioctl(f, DIOCWLABEL, (char *)&writeable) < 0)
306 				err(4, "ioctl DIOCWLABEL");
307 		}
308 		break;
309 	case WRITE:
310 		if (argc < 2 || argc > 3)
311 			usage();
312 		makelabel(argv[1], argc == 3 ? argv[2] : NULL, &lab);
313 		lp = makebootarea(bootarea, &lab, f);
314 		*lp = lab;
315 		if (checklabel(lp) == 0)
316 			error = writelabel(f, bootarea, lp);
317 		break;
318 #if NUMBOOT > 0
319 	case WRITEBOOT:
320 	{
321 		struct disklabel tlab;
322 
323 		if ((lp = readlabel(f)) == NULL)
324 			exit(1);
325 		tlab = *lp;
326 		if (argc == 2)
327 			makelabel(argv[1], NULL, &lab);
328 		lp = makebootarea(bootarea, &lab, f);
329 		*lp = tlab;
330 		if (checklabel(lp) == 0)
331 			error = writelabel(f, bootarea, lp);
332 		break;
333 	}
334 #endif
335 	default:
336 		break;
337 	}
338 	exit(error);
339 }
340 
341 /*
342  * Construct a prototype disklabel from /etc/disktab.  As a side
343  * effect, set the names of the primary and secondary boot files
344  * if specified.
345  */
346 void
347 makelabel(type, name, lp)
348 	char *type, *name;
349 	struct disklabel *lp;
350 {
351 	struct disklabel *dp;
352 
353 	dp = getdiskbyname(type);
354 	if (dp == NULL)
355 		errx(1, "unknown disk type: %s", type);
356 	*lp = *dp;
357 #if NUMBOOT > 0
358 	/*
359 	 * Set bootstrap name(s).
360 	 * 1. If set from command line, use those,
361 	 * 2. otherwise, check if disktab specifies them (b0 or b1),
362 	 * 3. otherwise, makebootarea() will choose ones based on the name
363 	 *    of the disk special file. E.g. /dev/ra0 -> raboot, bootra
364 	 */
365 	if (!xxboot && lp->d_boot0) {
366 		if (*lp->d_boot0 != '/')
367 			(void)sprintf(boot0, "%s%s",
368 				      _PATH_BOOTDIR, lp->d_boot0);
369 		else
370 			(void)strcpy(boot0, lp->d_boot0);
371 		xxboot = boot0;
372 	}
373 #if NUMBOOT > 1
374 	if (!bootxx && lp->d_boot1) {
375 		if (*lp->d_boot1 != '/')
376 			(void)sprintf(boot1, "%s%s",
377 				      _PATH_BOOTDIR, lp->d_boot1);
378 		else
379 			(void)strcpy(boot1, lp->d_boot1);
380 		bootxx = boot1;
381 	}
382 #endif
383 #endif
384 	/* d_packname is union d_boot[01], so zero */
385 	memset(lp->d_packname, 0, sizeof(lp->d_packname));
386 	if (name)
387 		(void)strncpy(lp->d_packname, name, sizeof(lp->d_packname));
388 }
389 
390 int
391 writelabel(f, boot, lp)
392 	int f;
393 	char *boot;
394 	struct disklabel *lp;
395 {
396 	int writeable;
397 	off_t sectoffset = 0;
398 
399 	if (nwflag) {
400 		warnx("DANGER! The disklabel was not found at the correct location!");
401 		warnx("To repair this situation, use `disklabel %s > file' to",
402 		    dkname);
403 		warnx("save it, then use `disklabel -R %s file' to replace it.",
404 		    dkname);
405 		warnx("A new disklabel is not being installed now.");
406 		return(0); /* Actually 1 but we want to exit */
407 	}
408 #if NUMBOOT > 0
409 	setbootflag(lp);
410 #endif
411 	lp->d_magic = DISKMAGIC;
412 	lp->d_magic2 = DISKMAGIC;
413 	lp->d_checksum = 0;
414 	lp->d_checksum = dkcksum(lp);
415 	if (rflag) {
416 #ifdef DOSLABEL
417 		struct partition *pp = &lp->d_partitions[2];
418 
419 		/*
420 		 * If OpenBSD DOS partition is missing, or if
421 		 * the label to be written is not within partition,
422 		 * prompt first. Need to allow this in case operator
423 		 * wants to convert the drive for dedicated use.
424 		 * In this case, partition 'a' had better start at 0,
425 		 * otherwise we reject the request as meaningless. -wfj
426 		 */
427 		if (dosdp && pp->p_size &&
428 		    (dosdp->dp_typ == DOSPTYP_OPENBSD ||
429 		    dosdp->dp_typ == DOSPTYP_FREEBSD ||
430 		    dosdp->dp_typ == DOSPTYP_NETBSD)) {
431 		        sectoffset = (off_t)get_le(&dosdp->dp_start) *
432 			    lp->d_secsize;
433 		} else {
434 			if (dosdp) {
435 				int first, ch;
436 
437 				printf("Erase the previous contents of the disk? [n]: ");
438 				fflush(stdout);
439 				first = ch = getchar();
440 				while (ch != '\n' && ch != EOF)
441 					ch = getchar();
442 				if (first != 'y' && first != 'Y')
443 					exit(0);
444 			}
445 			sectoffset = 0;
446 		}
447 
448 #if NUMBOOT > 0
449 		/*
450 		 * If we are not installing a boot program
451 		 * we must read the current bootarea so we don't
452 		 * clobber the existing boot.
453 		 */
454 		if (!installboot) {
455 			struct disklabel tlab;
456 
457 			if (lseek(f, sectoffset, SEEK_SET) < 0) {
458 				perror("lseek");
459 				return (1);
460 			}
461 			tlab = *lp;
462 			if (read(f, boot, tlab.d_bbsize) != tlab.d_bbsize) {
463 				perror("read");
464 				return (1);
465 			}
466 			*lp =tlab;
467 		}
468 #endif
469 #endif
470 
471 		/*
472 		 * First set the kernel disk label,
473 		 * then write a label to the raw disk.
474 		 * If the SDINFO ioctl fails because it is unimplemented,
475 		 * keep going; otherwise, the kernel consistency checks
476 		 * may prevent us from changing the current (in-core)
477 		 * label.
478 		 */
479 		if (!donothing) {
480 			if (ioctl(f, DIOCSDINFO, lp) < 0 &&
481 			    errno != ENODEV && errno != ENOTTY) {
482 				l_perror("ioctl DIOCSDINFO");
483 				return (1);
484 			}
485 		}
486 		if (verbose)
487 			printf("writing label to block %lld (0x%qx)\n",
488 			    (long long)sectoffset/DEV_BSIZE,
489 			    (long long)sectoffset/DEV_BSIZE);
490 		if (!donothing) {
491 			if (lseek(f, sectoffset, SEEK_SET) < 0) {
492 				perror("lseek");
493 				return (1);
494 			}
495 			/*
496 			 * write enable label sector before write (if necessary),
497 			 * disable after writing.
498 			 */
499 			writeable = 1;
500 
501 			if (ioctl(f, DIOCWLABEL, &writeable) < 0)
502 				perror("ioctl DIOCWLABEL");
503 #ifdef __alpha__
504 			/*
505 			 * The Alpha requires that the boot block be checksummed.
506 			 * The first 63 8-byte quantites are summed into the 64th.
507 			 */
508 			{
509 				int i;
510 				u_int64_t *dp, sum;
511 
512 				dp = (u_int64_t *)boot;
513 				sum = 0;
514 				for (i = 0; i < 63; i++)
515 					sum += dp[i];
516 				dp[63] = sum;
517 			}
518 #endif
519 			if (write(f, boot, lp->d_bbsize) != lp->d_bbsize) {
520 				perror("write");
521 				return (1);
522 			}
523 		}
524 #if NUMBOOT > 0
525 		/*
526 		 * Output the remainder of the disklabel
527 		 */
528 		if (!donothing && bootbuf && write(f, bootbuf, bootsize) != bootsize) {
529 			perror("write");
530 			return(1);
531 		}
532 #endif
533 		writeable = 0;
534 		if (!donothing)
535 			if (ioctl(f, DIOCWLABEL, &writeable) < 0)
536 				perror("ioctl DIOCWLABEL");
537 	} else {
538 		if (!donothing) {
539 			if (ioctl(f, DIOCWDINFO, lp) < 0) {
540 				l_perror("ioctl DIOCWDINFO");
541 				return (1);
542 			}
543 		}
544 	}
545 #ifdef __vax__
546 	if (lp->d_type == DTYPE_SMD && lp->d_flags & D_BADSECT) {
547 		daddr_t alt;
548 		int i;
549 
550 		alt = lp->d_ncylinders * lp->d_secpercyl - lp->d_nsectors;
551 		for (i = 1; i < 11 && i < lp->d_nsectors; i += 2) {
552 			(void)lseek(f, (off_t)((alt + i) * lp->d_secsize),
553 			    SEEK_SET);
554 			if (!donothing)
555 				if (write(f, boot, lp->d_secsize) < lp->d_secsize)
556 					warn("alternate label %d write", i/2);
557 		}
558 	}
559 #endif
560 	return (0);
561 }
562 
563 void
564 l_perror(s)
565 	char *s;
566 {
567 
568 	switch (errno) {
569 	case ESRCH:
570 		warnx("%s: No disk label on disk;\n"
571 		    "use \"disklabel -r\" to install initial label", s);
572 		break;
573 	case EINVAL:
574 		warnx("%s: Label magic number or checksum is wrong!\n"
575 		    "(disklabel or kernel is out of date?)", s);
576 		break;
577 	case EBUSY:
578 		warnx("%s: Open partition would move or shrink", s);
579 		break;
580 	case EXDEV:
581 		warnx("%s: Labeled partition or 'a' partition must start "
582 		    "at beginning of disk", s);
583 		break;
584 	default:
585 		warn("%s", s);
586 		break;
587 	}
588 }
589 
590 #ifdef DOSLABEL
591 /*
592  * Fetch DOS partition table from disk.
593  */
594 struct dos_partition *
595 readmbr(f)
596 	int f;
597 {
598 	static int mbr[DEV_BSIZE / sizeof(int)];
599 	struct dos_partition *dp;
600 	u_int16_t signature;
601 	int part;
602 
603 	/*
604 	 * This must be done this way due to alignment restrictions
605 	 * in for example mips processors.
606          */
607 	dp = (struct dos_partition *)mbr;
608 	if (lseek(f, (off_t)DOSBBSECTOR * DEV_BSIZE, SEEK_SET) < 0 ||
609 	    read(f, mbr, sizeof(mbr)) < sizeof(mbr))
610 		err(4, "can't read master boot record");
611 	signature = *((u_char *)mbr + DOSMBR_SIGNATURE_OFF) |
612 	    (*((u_char *)mbr + DOSMBR_SIGNATURE_OFF + 1) << 8);
613 	bcopy((char *)mbr+DOSPARTOFF, (char *)mbr, sizeof(*dp) * NDOSPART);
614 
615 	/*
616 	 * Don't (yet) know disk geometry (BIOS), use
617 	 * partition table to find OpenBSD partition, and obtain
618 	 * disklabel from there.
619 	 */
620 	/* Check if table is valid. */
621 	for (part = 0; part < NDOSPART; part++) {
622 		if ((dp[part].dp_flag & ~0x80) != 0)
623 			return (0);
624 	}
625 	/* Find OpenBSD partition. */
626 	for (part = 0; part < NDOSPART; part++) {
627 		if (get_le(&dp[part].dp_size) && dp[part].dp_typ == DOSPTYP_OPENBSD) {
628 			fprintf(stderr, "# using MBR partition %d: "
629 			    "type %02X off %d (0x%x) size %d (0x%x)\n", part,
630 			    dp[part].dp_typ,
631 			    get_le(&dp[part].dp_start), get_le(&dp[part].dp_start),
632 			    get_le(&dp[part].dp_size), get_le(&dp[part].dp_size));
633 			return (&dp[part]);
634 		}
635 	}
636 	for (part = 0; part < NDOSPART; part++) {
637 		if (get_le(&dp[part].dp_size) && dp[part].dp_typ == DOSPTYP_FREEBSD) {
638 			fprintf(stderr, "# using MBR partition %d: "
639 			    "type %02X off %d (0x%x) size %d (0x%x)\n", part,
640 			    dp[part].dp_typ,
641 			    get_le(&dp[part].dp_start), get_le(&dp[part].dp_start),
642 			    get_le(&dp[part].dp_size), get_le(&dp[part].dp_size));
643 			return (&dp[part]);
644 		}
645 	}
646 	for (part = 0; part < NDOSPART; part++) {
647 		if (get_le(&dp[part].dp_size) && dp[part].dp_typ == DOSPTYP_NETBSD) {
648 			fprintf(stderr, "# using MBR partition %d: "
649 			    "type %02X off %d (0x%x) size %d (0x%x)\n", part,
650 			    dp[part].dp_typ,
651 			    get_le(&dp[part].dp_start), get_le(&dp[part].dp_start),
652 			    get_le(&dp[part].dp_size), get_le(&dp[part].dp_size));
653 			return (&dp[part]);
654 		}
655 	}
656 
657 	/*
658 	 * If there is no signature and no OpenBSD partition this is probably
659 	 * not an MBR.
660 	 */
661 	if (signature != DOSMBR_SIGNATURE)
662 		return (NULL);
663 
664 	/* If no OpenBSD partition, find first used partition. */
665 	for (part = 0; part < NDOSPART; part++) {
666 		if (get_le(&dp[part].dp_size)) {
667 			warnx("warning, DOS partition table with no valid OpenBSD partition");
668 			return (&dp[part]);
669 		}
670 	}
671 	/* Table appears to be empty. */
672 	return (NULL);
673 }
674 #endif
675 
676 /*
677  * Fetch disklabel for disk.
678  * Use ioctl to get label unless -r flag is given.
679  */
680 struct disklabel *
681 readlabel(f)
682 	int f;
683 {
684 	struct disklabel *lp = NULL;
685 
686 	if (rflag) {
687 		char *msg;
688 		off_t sectoffset = 0;
689 
690 #ifdef DOSLABEL
691 		if (dosdp && get_le(&dosdp->dp_size) &&
692 		    (dosdp->dp_typ == DOSPTYP_OPENBSD ||
693 		    dosdp->dp_typ == DOSPTYP_FREEBSD ||
694 		    dosdp->dp_typ == DOSPTYP_NETBSD))
695 			sectoffset = (off_t)get_le(&dosdp->dp_start) *
696 			    DEV_BSIZE;
697 #endif
698 		if (verbose)
699 			printf("reading label from block %lld, offset %lld\n",
700 			    (long long)sectoffset/DEV_BSIZE,
701 			    sectoffset/DEV_BSIZE +
702 			    (LABELSECTOR * DEV_BSIZE) + LABELOFFSET);
703 		if (lseek(f, sectoffset, SEEK_SET) < 0 ||
704 		    read(f, bootarea, BBSIZE) < BBSIZE)
705 			err(4, "%s", specname);
706 
707 		lp = (struct disklabel *)(bootarea +
708 			(LABELSECTOR * DEV_BSIZE) + LABELOFFSET);
709 		if (lp->d_magic == DISKMAGIC &&
710 		    lp->d_magic2 == DISKMAGIC) {
711 			if (lp->d_npartitions <= MAXPARTITIONS &&
712 			    dkcksum(lp) == 0)
713 				return (lp);
714 
715 			msg = "disk label corrupted";
716 		}
717 		else {
718 			warnx("no disklabel found. scanning.");
719 		}
720 		nwflag++;
721 
722 		msg = "no disk label";
723 		for (lp = (struct disklabel *)bootarea;
724 		    lp <= (struct disklabel *)(bootarea + BBSIZE - sizeof(*lp));
725 		    lp = (struct disklabel *)((char *)lp + sizeof(long))) {
726 			if (lp->d_magic == DISKMAGIC &&
727 			    lp->d_magic2 == DISKMAGIC) {
728 				if (lp->d_npartitions <= MAXPARTITIONS &&
729 				    dkcksum(lp) == 0) {
730 					warnx("found at 0x%lx",
731 					    (long)((char *)lp - bootarea));
732 					return (lp);
733 				}
734 				msg = "disk label corrupted";
735 			}
736 		}
737 		warnx("%s", msg);
738 		return(NULL);
739 	} else {
740 		if (cflag && ioctl(f, DIOCRLDINFO) < 0)
741 			err(4, "ioctl DIOCRLDINFO");
742 		if (dflag) {
743 			lp = &lab;
744 			if (ioctl(f, DIOCGPDINFO, lp) < 0)
745 				err(4, "ioctl DIOCGPDINFO");
746 		} else {
747 			lp = &lab;
748 			if (ioctl(f, DIOCGDINFO, lp) < 0)
749 				err(4, "ioctl DIOCGDINFO");
750 		}
751 	}
752 	return (lp);
753 }
754 
755 /*
756  * Construct a bootarea (d_bbsize bytes) in the specified buffer ``boot''
757  * Returns a pointer to the disklabel portion of the bootarea.
758  */
759 struct disklabel *
760 makebootarea(boot, dp, f)
761 	char *boot;
762 	struct disklabel *dp;
763 	int f;
764 {
765 	struct disklabel *lp;
766 	char *p;
767 #if NUMBOOT > 0
768 	char *dkbasename;
769 	int b;
770 #if NUMBOOT == 1
771 	struct stat sb;
772 #endif
773 #endif
774 
775 	/* XXX */
776 	if (dp->d_secsize == 0) {
777 		dp->d_secsize = DEV_BSIZE;
778 		dp->d_bbsize = BBSIZE;
779 	}
780 	lp = (struct disklabel *)
781 	    (boot + (LABELSECTOR * dp->d_secsize) + LABELOFFSET);
782 	memset(lp, 0, sizeof *lp);
783 #if NUMBOOT > 0
784 	/*
785 	 * If we are not installing a boot program but we are installing a
786 	 * label on disk then we must read the current bootarea so we don't
787 	 * clobber the existing boot.
788 	 */
789 	if (!installboot) {
790 #ifndef __i386__
791 		if (rflag) {
792 			if (read(f, boot, BBSIZE) < BBSIZE)
793 				err(4, "%s", specname);
794 			memset(lp, 0, sizeof *lp);
795 		}
796 #endif
797 		return (lp);
798 	}
799 	/*
800 	 * We are installing a boot program.  Determine the name(s) and
801 	 * read them into the appropriate places in the boot area.
802 	 */
803 	if (!xxboot || !bootxx) {
804 		dkbasename = np;
805 		if ((p = strrchr(dkname, '/')) == NULL)
806 			p = dkname;
807 		else
808 			p++;
809 		while (*p && !isdigit(*p))
810 			*np++ = *p++;
811 		*np++ = '\0';
812 
813 		if (!xxboot) {
814 			(void)sprintf(np, "%s%sboot",
815 				      _PATH_BOOTDIR, dkbasename);
816 			if (access(np, F_OK) < 0 && dkbasename[0] == 'r')
817 				dkbasename++;
818 			xxboot = np;
819 			(void)sprintf(xxboot, "%s%sboot",
820 				      _PATH_BOOTDIR, dkbasename);
821 			np += strlen(xxboot) + 1;
822 		}
823 #if NUMBOOT > 1
824 		if (!bootxx) {
825 			(void)sprintf(np, "%sboot%s",
826 				      _PATH_BOOTDIR, dkbasename);
827 			if (access(np, F_OK) < 0 && dkbasename[0] == 'r')
828 				dkbasename++;
829 			bootxx = np;
830 			(void)sprintf(bootxx, "%sboot%s",
831 				      _PATH_BOOTDIR, dkbasename);
832 			np += strlen(bootxx) + 1;
833 		}
834 #endif
835 	}
836 	if (verbose)
837 		warnx("bootstraps: xxboot = %s, bootxx = %s", xxboot,
838 		    bootxx ? bootxx : "NONE");
839 
840 	/*
841 	 * Strange rules:
842 	 * 1. One-piece bootstrap (hp300/hp800)
843 	 *	up to d_bbsize bytes of ``xxboot'' go in bootarea, the rest
844 	 *	is remembered and written later following the bootarea.
845 	 * 2. Two-piece bootstraps (vax/i386?/mips?)
846 	 *	up to d_secsize bytes of ``xxboot'' go in first d_secsize
847 	 *	bytes of bootarea, remaining d_bbsize-d_secsize filled
848 	 *	from ``bootxx''.
849 	 */
850 	b = open(xxboot, O_RDONLY);
851 	if (b < 0)
852 		err(4, "%s", xxboot);
853 #if NUMBOOT > 1
854 	if (read(b, boot, (int)dp->d_secsize) < 0)
855 		err(4, "%s", xxboot);
856 	(void)close(b);
857 	b = open(bootxx, O_RDONLY);
858 	if (b < 0)
859 		err(4, "%s", bootxx);
860 	if (read(b, &boot[dp->d_secsize], (int)(dp->d_bbsize-dp->d_secsize)) < 0)
861 		err(4, "%s", bootxx);
862 #else
863 	if (read(b, boot, (int)dp->d_bbsize) < 0)
864 		err(4, "%s", xxboot);
865 	(void)fstat(b, &sb);
866 	bootsize = (int)sb.st_size - dp->d_bbsize;
867 	if (bootsize > 0) {
868 		/* XXX assume d_secsize is a power of two */
869 		bootsize = (bootsize + dp->d_secsize-1) & ~(dp->d_secsize-1);
870 		bootbuf = (char *)malloc((size_t)bootsize);
871 		if (bootbuf == 0)
872 			err(4, "%s", xxboot);
873 		if (read(b, bootbuf, bootsize) < 0) {
874 			free(bootbuf);
875 			err(4, "%s", xxboot);
876 		}
877 	}
878 #endif
879 	(void)close(b);
880 #endif
881 	/*
882 	 * Make sure no part of the bootstrap is written in the area
883 	 * reserved for the label.
884 	 */
885 	for (p = (char *)lp; p < (char *)lp + sizeof(struct disklabel); p++)
886 		if (*p)
887 			errx(2, "Bootstrap doesn't leave room for disk label");
888 	return (lp);
889 }
890 
891 void
892 makedisktab(f, lp)
893 	FILE *f;
894 	struct disklabel *lp;
895 {
896 	int i;
897 	char *did = "\\\n\t:";
898 	struct partition *pp;
899 
900 	if (lp->d_packname[0])
901 		(void)fprintf(f, "%.*s|", (int)sizeof(lp->d_packname),
902 		    lp->d_packname);
903 	if (lp->d_typename[0])
904 		(void)fprintf(f, "%.*s|", (int)sizeof(lp->d_typename),
905 		    lp->d_typename);
906 	(void)fputs("Automatically generated label:\\\n\t:dt=", f);
907 	if ((unsigned) lp->d_type < DKMAXTYPES)
908 		(void)fprintf(f, "%s:", dktypenames[lp->d_type]);
909 	else
910 		(void)fprintf(f, "unknown%d:", lp->d_type);
911 
912 	(void)fprintf(f, "se#%d:", lp->d_secsize);
913 	(void)fprintf(f, "ns#%d:", lp->d_nsectors);
914 	(void)fprintf(f, "nt#%d:", lp->d_ntracks);
915 	(void)fprintf(f, "sc#%d:", lp->d_secpercyl);
916 	(void)fprintf(f, "nc#%d:", lp->d_ncylinders);
917 
918 	if (lp->d_rpm != 3600) {
919 		(void)fprintf(f, "%srm#%d:", did, lp->d_rpm);
920 		did = "";
921 	}
922 	if (lp->d_interleave != 1) {
923 		(void)fprintf(f, "%sil#%d:", did, lp->d_interleave);
924 		did = "";
925 	}
926 	if (lp->d_trackskew != 0) {
927 		(void)fprintf(f, "%ssk#%d:", did, lp->d_trackskew);
928 		did = "";
929 	}
930 	if (lp->d_cylskew != 0) {
931 		(void)fprintf(f, "%scs#%d:", did, lp->d_cylskew);
932 		did = "";
933 	}
934 	if (lp->d_headswitch != 0) {
935 		(void)fprintf(f, "%shs#%d:", did, lp->d_headswitch);
936 		did = "";
937 	}
938 	if (lp->d_trkseek != 0) {
939 		(void)fprintf(f, "%sts#%d:", did, lp->d_trkseek);
940 		did = "";
941 	}
942 	for (i = 0; i < NDDATA; i++)
943 		if (lp->d_drivedata[i])
944 			(void)fprintf(f, "d%d#%d", i, lp->d_drivedata[i]);
945 	pp = lp->d_partitions;
946 	for (i = 0; i < lp->d_npartitions; i++, pp++) {
947 		if (pp->p_size) {
948 			char c = 'a' + i;
949 
950 			(void)fprintf(f, "\\\n\t:");
951 			(void)fprintf(f, "p%c#%d:", c, pp->p_size);
952 			(void)fprintf(f, "o%c#%d:", c, pp->p_offset);
953 			if (pp->p_fstype != FS_UNUSED) {
954 				if ((unsigned) pp->p_fstype < FSMAXTYPES)
955 					(void)fprintf(f, "t%c=%s:", c,
956 					    fstypenames[pp->p_fstype]);
957 				else
958 					(void)fprintf(f, "t%c=unknown%d:",
959 					    c, pp->p_fstype);
960 			}
961 			switch (pp->p_fstype) {
962 
963 			case FS_UNUSED:
964 				break;
965 
966 			case FS_BSDFFS:
967 				(void)fprintf(f, "b%c#%d:", c,
968 				    pp->p_fsize * pp->p_frag);
969 				(void)fprintf(f, "f%c#%d:", c, pp->p_fsize);
970 				break;
971 
972 			default:
973 				break;
974 			}
975 		}
976 	}
977 	(void)fputc('\n', f);
978 	(void)fflush(f);
979 }
980 
981 int
982 width_partition(lp, unit)
983 	struct disklabel *lp;
984 {
985 	unit = toupper(unit);
986 	switch (unit) {
987 	case 'K':
988 		return 10;
989 	}
990 	return 8;
991 }
992 
993 /*
994  * Display a particular partion.
995  */
996 void
997 display_partition(f, lp, mp, i, unit, width)
998 	FILE *f;
999 	struct disklabel *lp;
1000 	char **mp;
1001 	int i;
1002 	char unit;
1003 	int width;
1004 {
1005 	struct partition *pp = &lp->d_partitions[i];
1006 	double p_size, p_offset;
1007 
1008 	if (width == 0)
1009 		width = 8;
1010 	unit = toupper(unit);
1011 	switch (unit) {
1012 	case 'B':
1013 		p_size = (double)pp->p_size * lp->d_secsize;
1014 		p_offset = (double)pp->p_offset * lp->d_secsize;
1015 		break;
1016 
1017 	case 'C':
1018 		p_size = (double)pp->p_size / lp->d_secpercyl;
1019 		p_offset = (double)pp->p_offset / lp->d_secpercyl;
1020 		break;
1021 
1022 	case 'K':
1023 		p_size = (double)pp->p_size / (1024 / lp->d_secsize);
1024 		p_offset = (double)pp->p_offset / (1024 / lp->d_secsize);
1025 		break;
1026 
1027 	case 'M':
1028 		p_size = (double)pp->p_size / ((1024*1024) / lp->d_secsize);
1029 		p_offset = (double)pp->p_offset / ((1024*1024) / lp->d_secsize);
1030 		break;
1031 
1032 	case 'G':
1033 		p_size = (double)pp->p_size / ((1024*1024*1024) / lp->d_secsize);
1034 		p_offset = (double)pp->p_offset / ((1024*1024*1024) / lp->d_secsize);
1035 		break;
1036 
1037 	default:
1038 		p_size = -1;			/* no conversion */
1039 		p_offset = 0;
1040 		break;
1041 	}
1042 
1043 	if (pp->p_size) {
1044 		if (p_size < 0)
1045 			fprintf(f, "  %c: %*u %*u  ", 'a' + i,
1046 			    width, pp->p_size, width, pp->p_offset);
1047 		else
1048 			fprintf(f, "  %c: %*.1f%c %*.1f%c  ", 'a' + i,
1049 			    width-1, p_size, unit, width-1, p_offset, unit);
1050 		if ((unsigned) pp->p_fstype < FSMAXTYPES)
1051 			fprintf(f, "%8.8s", fstypenames[pp->p_fstype]);
1052 		else
1053 			fprintf(f, "%8d", pp->p_fstype);
1054 		switch (pp->p_fstype) {
1055 
1056 		case FS_UNUSED:				/* XXX */
1057 			fprintf(f, "    %5d %5d %5.5s ",
1058 			    pp->p_fsize, pp->p_fsize * pp->p_frag, "");
1059 			break;
1060 
1061 		case FS_BSDFFS:
1062 			fprintf(f, "    %5d %5d %5d ",
1063 			    pp->p_fsize, pp->p_fsize * pp->p_frag,
1064 			    pp->p_cpg);
1065 			break;
1066 
1067 		default:
1068 			fprintf(f, "%22.22s", "");
1069 			break;
1070 		}
1071 		if (mp != NULL) {
1072 			if (mp[i] != NULL)
1073 				fprintf(f, " # %s", mp[i]);
1074 		} else if (lp->d_secpercyl) {
1075 			fprintf(f, "\t# (Cyl. %4d",
1076 			    pp->p_offset / lp->d_secpercyl);
1077 			if (pp->p_offset % lp->d_secpercyl)
1078 				putc('*', f);
1079 			else
1080 				putc(' ', f);
1081 			fprintf(f, "- %d",
1082 			    (pp->p_offset +
1083 			    pp->p_size + lp->d_secpercyl - 1) /
1084 			    lp->d_secpercyl - 1);
1085 			if ((pp->p_offset + pp->p_size) % lp->d_secpercyl)
1086 				putc('*', f);
1087 			putc(')', f);
1088 		}
1089 		putc('\n', f);
1090 	}
1091 }
1092 
1093 void
1094 display(f, lp)
1095 	FILE *f;
1096 	struct disklabel *lp;
1097 {
1098 	int i, j;
1099 	int width;
1100 
1101 	fprintf(f, "# %s:\n", specname);
1102 	if ((unsigned) lp->d_type < DKMAXTYPES)
1103 		fprintf(f, "type: %s\n", dktypenames[lp->d_type]);
1104 	else
1105 		fprintf(f, "type: %d\n", lp->d_type);
1106 	fprintf(f, "disk: %.*s\n", (int)sizeof(lp->d_typename), lp->d_typename);
1107 	fprintf(f, "label: %.*s\n", (int)sizeof(lp->d_packname), lp->d_packname);
1108 	fprintf(f, "flags:");
1109 	if (lp->d_flags & D_REMOVABLE)
1110 		fprintf(f, " removable");
1111 	if (lp->d_flags & D_ECC)
1112 		fprintf(f, " ecc");
1113 	if (lp->d_flags & D_BADSECT)
1114 		fprintf(f, " badsect");
1115 	putc('\n', f);
1116 	fprintf(f, "bytes/sector: %ld\n", (long)lp->d_secsize);
1117 	fprintf(f, "sectors/track: %ld\n", (long)lp->d_nsectors);
1118 	fprintf(f, "tracks/cylinder: %ld\n", (long)lp->d_ntracks);
1119 	fprintf(f, "sectors/cylinder: %ld\n", (long)lp->d_secpercyl);
1120 	fprintf(f, "cylinders: %ld\n", (long)lp->d_ncylinders);
1121 	fprintf(f, "total sectors: %ld\n", (long)lp->d_secperunit);
1122 	fprintf(f, "rpm: %ld\n", (long)lp->d_rpm);
1123 	fprintf(f, "interleave: %ld\n", (long)lp->d_interleave);
1124 	fprintf(f, "trackskew: %ld\n", (long)lp->d_trackskew);
1125 	fprintf(f, "cylinderskew: %ld\n", (long)lp->d_cylskew);
1126 	fprintf(f, "headswitch: %ld\t\t# microseconds\n",
1127 	    (long)lp->d_headswitch);
1128 	fprintf(f, "track-to-track seek: %ld\t# microseconds\n",
1129 	    (long)lp->d_trkseek);
1130 	fprintf(f, "drivedata: ");
1131 	for (i = NDDATA - 1; i >= 0; i--)
1132 		if (lp->d_drivedata[i])
1133 			break;
1134 	if (i < 0)
1135 		i = 0;
1136 	for (j = 0; j <= i; j++)
1137 		fprintf(f, "%d ", lp->d_drivedata[j]);
1138 	fprintf(f, "\n\n%d partitions:\n", lp->d_npartitions);
1139 	width = width_partition(lp, 0);
1140 	fprintf(f,
1141 	    "#    %*.*s %*.*s    fstype   [fsize bsize   cpg]\n",
1142 	    width, width, "size", width, width, "offset");
1143 	for (i = 0; i < lp->d_npartitions; i++)
1144 		display_partition(f, lp, NULL, i, 0, width);
1145 	fflush(f);
1146 }
1147 
1148 int
1149 edit(lp, f)
1150 	struct disklabel *lp;
1151 	int f;
1152 {
1153 	int first, ch, fd;
1154 	struct disklabel label;
1155 	FILE *fp;
1156 
1157 	if ((fd = mkstemp(tmpfil)) == -1 || (fp = fdopen(fd, "w")) == NULL) {
1158 		if (fd != -1)
1159 			close(fd);
1160 		warn("%s", tmpfil);
1161 		return (1);
1162 	}
1163 	display(fp, lp);
1164 	fprintf(fp, "\n# Notes:\n");
1165 	fprintf(fp,
1166 "# Up to 16 partitions are valid, named from 'a' to 'p'.  Partition 'a' is\n"
1167 "# your root filesystem, 'b' is your swap, and 'c' should cover your whole\n"
1168 "# disk. Any other partition is free for any use.  'size' and 'offset' are\n"
1169 "# in 512-byte blocks. fstype should be '4.2BSD', 'swap', or 'none' or some\n"
1170 "# other values.  fsize/bsize/cpg should typically be '1024 8192 16' for a\n"
1171 "# 4.2BSD filesystem (or '512 4096 16' except on alpha, sun4, amiga, sun3...)\n");
1172 	fclose(fp);
1173 	for (;;) {
1174 		if (!editit())
1175 			break;
1176 		fp = fopen(tmpfil, "r");
1177 		if (fp == NULL) {
1178 			warn("%s", tmpfil);
1179 			break;
1180 		}
1181 		memset(&label, 0, sizeof(label));
1182 		if (getasciilabel(fp, &label)) {
1183 			if (cmplabel(lp, &label) == 0) {
1184 				puts("No changes.");
1185 				fclose(fp);
1186 				(void) unlink(tmpfil);
1187 				return (0);
1188 			}
1189 			*lp = label;
1190 			if (writelabel(f, bootarea, lp) == 0) {
1191 				fclose(fp);
1192 				(void) unlink(tmpfil);
1193 				return (0);
1194 			}
1195 		}
1196 		fclose(fp);
1197 		printf("re-edit the label? [y]: ");
1198 		fflush(stdout);
1199 		first = ch = getchar();
1200 		while (ch != '\n' && ch != EOF)
1201 			ch = getchar();
1202 		if (first == 'n' || first == 'N')
1203 			break;
1204 	}
1205 	(void)unlink(tmpfil);
1206 	return (1);
1207 }
1208 
1209 int
1210 editit()
1211 {
1212 	pid_t pid, xpid;
1213 	int stat;
1214 	extern char *getenv();
1215 	char *argp[] = {"sh", "-c", NULL, NULL};
1216 	char *ed, *p;
1217 
1218 	if ((ed = getenv("EDITOR")) == NULL)
1219 		ed = _PATH_VI;
1220 	p = (char *)malloc(strlen(ed) + 1 + strlen(tmpfil) + 1);
1221 	if (!p) {
1222 		warn("failed to start editor");
1223 		return (0);
1224 	}
1225 	sprintf(p, "%s %s", ed, tmpfil);
1226 	argp[2] = p;
1227 
1228 	/* Turn off signals. */
1229 	(void)signal(SIGHUP, SIG_IGN);
1230 	(void)signal(SIGINT, SIG_IGN);
1231 	(void)signal(SIGQUIT, SIG_IGN);
1232 	while ((pid = fork()) < 0) {
1233 		if (errno != EAGAIN) {
1234 			warn("fork");
1235 			free(p);
1236 			stat = 1;
1237 			goto bail;
1238 		}
1239 		sleep(1);
1240 	}
1241 	if (pid == 0) {
1242 		execv(_PATH_BSHELL, argp);
1243 		_exit(127);
1244 	}
1245 	free(p);
1246 	for (;;) {
1247 		xpid = waitpid(pid, (int *)&stat, WUNTRACED);
1248 		if (WIFSTOPPED(stat))
1249 			raise(WSTOPSIG(stat));
1250 		else if (WIFEXITED(stat))
1251 			break;
1252 	}
1253 bail:
1254 	(void)signal(SIGHUP, SIG_DFL);
1255 	(void)signal(SIGINT, SIG_DFL);
1256 	(void)signal(SIGQUIT, SIG_DFL);
1257 	return (!stat);
1258 }
1259 
1260 char *
1261 skip(cp)
1262 	char *cp;
1263 {
1264 
1265 	cp += strspn(cp, " \t");
1266 	if (*cp == '\0')
1267 		return (NULL);
1268 	return (cp);
1269 }
1270 
1271 char *
1272 word(cp)
1273 	char *cp;
1274 {
1275 
1276 	cp += strcspn(cp, " \t");
1277 	if (*cp == '\0')
1278 		return (NULL);
1279 	*cp++ = '\0';
1280 	cp += strspn(cp, " \t");
1281 	if (*cp == '\0')
1282 		return (NULL);
1283 	return (cp);
1284 }
1285 
1286 /*
1287  * Read an ascii label in from fd f,
1288  * in the same format as that put out by display(),
1289  * and fill in lp.
1290  */
1291 int
1292 getasciilabel(f, lp)
1293 	FILE *f;
1294 	struct disklabel *lp;
1295 {
1296 	char **cpp, *cp;
1297 	struct partition *pp;
1298 	char *tp, *s, line[BUFSIZ];
1299 	int v, lineno = 0, errors = 0;
1300 
1301 	lp->d_bbsize = BBSIZE;				/* XXX */
1302 	lp->d_sbsize = SBSIZE;				/* XXX */
1303 	while (fgets(line, sizeof(line) - 1, f)) {
1304 		lineno++;
1305 		if ((cp = strpbrk(line, "#\r\n")))
1306 			*cp = '\0';
1307 		cp = skip(line);
1308 		if (cp == NULL)
1309 			continue;
1310 		tp = strchr(cp, ':');
1311 		if (tp == NULL) {
1312 			warnx("line %d: syntax error", lineno);
1313 			errors++;
1314 			continue;
1315 		}
1316 		*tp++ = '\0', tp = skip(tp);
1317 		if (!strcmp(cp, "type")) {
1318 			if (tp == NULL)
1319 				tp = "unknown";
1320 			else if (strcasecmp(tp, "IDE") == 0)
1321 				tp = "ESDI";
1322 			cpp = dktypenames;
1323 			for (; cpp < &dktypenames[DKMAXTYPES]; cpp++)
1324 				if ((s = *cpp) && !strcasecmp(s, tp)) {
1325 					lp->d_type = cpp - dktypenames;
1326 					goto next;
1327 				}
1328 			v = atoi(tp);
1329 			if ((unsigned)v >= DKMAXTYPES)
1330 				warnx("line %d: warning, unknown disk type: %s",
1331 				    lineno, tp);
1332 			lp->d_type = v;
1333 			continue;
1334 		}
1335 		if (!strcmp(cp, "flags")) {
1336 			for (v = 0; (cp = tp) && *cp != '\0';) {
1337 				tp = word(cp);
1338 				if (!strcmp(cp, "removable"))
1339 					v |= D_REMOVABLE;
1340 				else if (!strcmp(cp, "ecc"))
1341 					v |= D_ECC;
1342 				else if (!strcmp(cp, "badsect"))
1343 					v |= D_BADSECT;
1344 				else {
1345 					warnx("line %d: bad flag: %s",
1346 					    lineno, cp);
1347 					errors++;
1348 				}
1349 			}
1350 			lp->d_flags = v;
1351 			continue;
1352 		}
1353 		if (!strcmp(cp, "drivedata")) {
1354 			int i;
1355 
1356 			for (i = 0; (cp = tp) && *cp != '\0' && i < NDDATA;) {
1357 				lp->d_drivedata[i++] = atoi(cp);
1358 				tp = word(cp);
1359 			}
1360 			continue;
1361 		}
1362 		if (sscanf(cp, "%d partitions", &v) == 1) {
1363 			if (v == 0 || (unsigned)v > MAXPARTITIONS) {
1364 				warnx("line %d: bad # of partitions", lineno);
1365 				lp->d_npartitions = MAXPARTITIONS;
1366 				errors++;
1367 			} else
1368 				lp->d_npartitions = v;
1369 			continue;
1370 		}
1371 		if (tp == NULL)
1372 			tp = "";
1373 		if (!strcmp(cp, "disk")) {
1374 			strncpy(lp->d_typename, tp, sizeof (lp->d_typename));
1375 			continue;
1376 		}
1377 		if (!strcmp(cp, "label")) {
1378 			strncpy(lp->d_packname, tp, sizeof (lp->d_packname));
1379 			continue;
1380 		}
1381 		if (!strcmp(cp, "bytes/sector")) {
1382 			v = atoi(tp);
1383 			if (v <= 0 || (v % 512) != 0) {
1384 				warnx("line %d: bad %s: %s", lineno, cp, tp);
1385 				errors++;
1386 			} else
1387 				lp->d_secsize = v;
1388 			continue;
1389 		}
1390 		if (!strcmp(cp, "sectors/track")) {
1391 			v = atoi(tp);
1392 			if (v <= 0) {
1393 				warnx("line %d: bad %s: %s", lineno, cp, tp);
1394 				errors++;
1395 			} else
1396 				lp->d_nsectors = v;
1397 			continue;
1398 		}
1399 		if (!strcmp(cp, "sectors/cylinder")) {
1400 			v = atoi(tp);
1401 			if (v <= 0) {
1402 				warnx("line %d: bad %s: %s", lineno, cp, tp);
1403 				errors++;
1404 			} else
1405 				lp->d_secpercyl = v;
1406 			continue;
1407 		}
1408 		if (!strcmp(cp, "tracks/cylinder")) {
1409 			v = atoi(tp);
1410 			if (v <= 0) {
1411 				warnx("line %d: bad %s: %s", lineno, cp, tp);
1412 				errors++;
1413 			} else
1414 				lp->d_ntracks = v;
1415 			continue;
1416 		}
1417 		if (!strcmp(cp, "cylinders")) {
1418 			v = atoi(tp);
1419 			if (v <= 0) {
1420 				warnx("line %d: bad %s: %s", lineno, cp, tp);
1421 				errors++;
1422 			} else
1423 				lp->d_ncylinders = v;
1424 			continue;
1425 		}
1426 		if (!strcmp(cp, "total sectors")) {
1427 			v = atoi(tp);
1428 			if (v <= 0) {
1429 				warnx("line %d: bad %s: %s", lineno, cp, tp);
1430 				errors++;
1431 			} else
1432 				lp->d_secperunit = v;
1433 			continue;
1434 		}
1435 		if (!strcmp(cp, "rpm")) {
1436 			v = atoi(tp);
1437 			if (v <= 0) {
1438 				warnx("line %d: bad %s: %s", lineno, cp, tp);
1439 				errors++;
1440 			} else
1441 				lp->d_rpm = v;
1442 			continue;
1443 		}
1444 		if (!strcmp(cp, "interleave")) {
1445 			v = atoi(tp);
1446 			if (v <= 0) {
1447 				warnx("line %d: bad %s: %s", lineno, cp, tp);
1448 				errors++;
1449 			} else
1450 				lp->d_interleave = v;
1451 			continue;
1452 		}
1453 		if (!strcmp(cp, "trackskew")) {
1454 			v = atoi(tp);
1455 			if (v < 0) {
1456 				warnx("line %d: bad %s: %s", lineno, cp, tp);
1457 				errors++;
1458 			} else
1459 				lp->d_trackskew = v;
1460 			continue;
1461 		}
1462 		if (!strcmp(cp, "cylinderskew")) {
1463 			v = atoi(tp);
1464 			if (v < 0) {
1465 				warnx("line %d: bad %s: %s", lineno, cp, tp);
1466 				errors++;
1467 			} else
1468 				lp->d_cylskew = v;
1469 			continue;
1470 		}
1471 		if (!strcmp(cp, "headswitch")) {
1472 			v = atoi(tp);
1473 			if (v < 0) {
1474 				warnx("line %d: bad %s: %s", lineno, cp, tp);
1475 				errors++;
1476 			} else
1477 				lp->d_headswitch = v;
1478 			continue;
1479 		}
1480 		if (!strcmp(cp, "track-to-track seek")) {
1481 			v = atoi(tp);
1482 			if (v < 0) {
1483 				warnx("line %d: bad %s: %s", lineno, cp, tp);
1484 				errors++;
1485 			} else
1486 				lp->d_trkseek = v;
1487 			continue;
1488 		}
1489 		if ('a' <= *cp && *cp <= 'z' && cp[1] == '\0') {
1490 			unsigned part = *cp - 'a';
1491 
1492 			if (part >= lp->d_npartitions) {
1493 				if (part >= MAXPARTITIONS) {
1494 					warnx("line %d: bad partition name: %s",
1495 					    lineno, cp);
1496 					errors++;
1497 					continue;
1498 				} else {
1499 					lp->d_npartitions = part + 1;
1500 				}
1501 			}
1502 			pp = &lp->d_partitions[part];
1503 #define NXTNUM(n) { \
1504 	if (tp == NULL) {					\
1505 		warnx("line %d: too few fields", lineno);	\
1506 		errors++;					\
1507 		break;						\
1508 	} else							\
1509 		cp = tp, tp = word(cp), (n) = atoi(cp);		\
1510 }
1511 			NXTNUM(v);
1512 			if (v < 0) {
1513 				warnx("line %d: bad partition size: %s",
1514 				    lineno, cp);
1515 				errors++;
1516 			} else
1517 				pp->p_size = v;
1518 			NXTNUM(v);
1519 			if (v < 0) {
1520 				warnx("line %d: bad partition offset: %s",
1521 				    lineno, cp);
1522 				errors++;
1523 			} else
1524 				pp->p_offset = v;
1525 			if (tp == NULL) {
1526 				pp->p_fstype = FS_UNUSED;
1527 				goto gottype;
1528 			}
1529 			cp = tp, tp = word(cp);
1530 			cpp = fstypenames;
1531 			for (; cpp < &fstypenames[FSMAXTYPES]; cpp++)
1532 				if ((s = *cpp) && !strcasecmp(s, cp)) {
1533 					pp->p_fstype = cpp - fstypenames;
1534 					goto gottype;
1535 				}
1536 			if (isdigit(*cp))
1537 				v = atoi(cp);
1538 			else
1539 				v = FSMAXTYPES;
1540 			if ((unsigned)v >= FSMAXTYPES) {
1541 				warnx("line %d: warning, unknown filesystem type: %s",
1542 				    lineno, cp);
1543 				v = FS_UNUSED;
1544 			}
1545 			pp->p_fstype = v;
1546 	gottype:
1547 			switch (pp->p_fstype) {
1548 
1549 			case FS_UNUSED:				/* XXX */
1550 				if (tp == NULL)	/* ok to skip fsize/bsize */
1551 					break;
1552 				NXTNUM(pp->p_fsize);
1553 				if (pp->p_fsize == 0)
1554 					break;
1555 				NXTNUM(v);
1556 				pp->p_frag = v / pp->p_fsize;
1557 				break;
1558 
1559 			case FS_BSDFFS:
1560 				NXTNUM(pp->p_fsize);
1561 				if (pp->p_fsize == 0)
1562 					break;
1563 				NXTNUM(v);
1564 				pp->p_frag = v / pp->p_fsize;
1565 				NXTNUM(pp->p_cpg);
1566 				break;
1567 
1568 			default:
1569 				break;
1570 			}
1571 			continue;
1572 		}
1573 		warnx("line %d: unknown field: %s", lineno, cp);
1574 		errors++;
1575 	next:
1576 		;
1577 	}
1578 	errors += checklabel(lp);
1579 	return (errors == 0);
1580 }
1581 
1582 /*
1583  * Check disklabel for errors and fill in
1584  * derived fields according to supplied values.
1585  */
1586 int
1587 checklabel(lp)
1588 	struct disklabel *lp;
1589 {
1590 	struct partition *pp;
1591 	int i, errors = 0;
1592 	char part;
1593 
1594 	if (lp->d_secsize == 0) {
1595 		warnx("sector size %d", lp->d_secsize);
1596 		return (1);
1597 	}
1598 	if (lp->d_nsectors == 0) {
1599 		warnx("sectors/track %d", lp->d_nsectors);
1600 		return (1);
1601 	}
1602 	if (lp->d_ntracks == 0) {
1603 		warnx("tracks/cylinder %d", lp->d_ntracks);
1604 		return (1);
1605 	}
1606 	if  (lp->d_ncylinders == 0) {
1607 		warnx("cylinders/unit %d", lp->d_ncylinders);
1608 		errors++;
1609 	}
1610 	if (lp->d_rpm == 0)
1611 		warnx("warning, revolutions/minute %d", lp->d_rpm);
1612 	if (lp->d_secpercyl == 0)
1613 		lp->d_secpercyl = lp->d_nsectors * lp->d_ntracks;
1614 	if (lp->d_secperunit == 0)
1615 		lp->d_secperunit = lp->d_secpercyl * lp->d_ncylinders;
1616 #ifdef i386__notyet
1617 	if (dosdp && dosdp->dp_size &&
1618 	    (dosdp->dp_typ == DOSPTYP_OPENBSD ||
1619 	    dosdp->dp_typ == DOSPTYP_FREEBSD ||
1620 	    dosdp->dp_typ == DOSPTYP_NETBSD)) {
1621 		&& lp->d_secperunit > dosdp->dp_start + dosdp->dp_size) {
1622 		warnx("exceeds DOS partition size");
1623 		errors++;
1624 		lp->d_secperunit = dosdp->dp_start + dosdp->dp_size;
1625 	}
1626 	/* XXX should also check geometry against BIOS's idea */
1627 #endif
1628 	if (lp->d_bbsize == 0) {
1629 		warnx("boot block size %d", lp->d_bbsize);
1630 		errors++;
1631 	} else if (lp->d_bbsize % lp->d_secsize)
1632 		warnx("warning, boot block size %% sector-size != 0");
1633 	if (lp->d_sbsize == 0) {
1634 		warnx("super block size %d", lp->d_sbsize);
1635 		errors++;
1636 	} else if (lp->d_sbsize % lp->d_secsize)
1637 		warnx("warning, super block size %% sector-size != 0");
1638 	if (lp->d_npartitions > MAXPARTITIONS)
1639 		warnx("warning, number of partitions (%d) > MAXPARTITIONS (%d)",
1640 		    lp->d_npartitions, MAXPARTITIONS);
1641 	for (i = 0; i < lp->d_npartitions; i++) {
1642 		part = 'a' + i;
1643 		pp = &lp->d_partitions[i];
1644 		if (pp->p_size == 0 && pp->p_offset != 0)
1645 			warnx("warning, partition %c: size 0, but offset %d",
1646 			    part, pp->p_offset);
1647 #ifdef CYLCHECK
1648 		if (pp->p_size % lp->d_secpercyl)
1649 			warnx("warning, partition %c: size %% cylinder-size != 0",
1650 			    part);
1651 		if (pp->p_offset % lp->d_secpercyl)
1652 			warnx("warning, partition %c: offset %% cylinder-size != 0",
1653 			    part);
1654 #endif
1655 #ifdef AAT0
1656 		if (i == 0 && pp->p_size != 0 && pp->p_offset != 0) {
1657 			warnx("this architecture requires partition 'a' to "
1658 			    "start at sector 0");
1659 			errors++;
1660 		}
1661 #endif
1662 		if (pp->p_offset > lp->d_secperunit) {
1663 			warnx("partition %c: offset past end of unit", part);
1664 			errors++;
1665 		}
1666 		if (pp->p_offset + pp->p_size > lp->d_secperunit) {
1667 			warnx("partition %c: partition extends past end of unit",
1668 			    part);
1669 			errors++;
1670 		}
1671 		if (pp->p_frag == 0 && pp->p_fsize != 0) {
1672 			warnx("partition %c: block size < fragment size", part);
1673 			errors++;
1674 		}
1675 	}
1676 	for (; i < MAXPARTITIONS; i++) {
1677 		part = 'a' + i;
1678 		pp = &lp->d_partitions[i];
1679 		if (pp->p_size || pp->p_offset)
1680 			warnx("warning, unused partition %c: size %d offset %d",
1681 			    'a' + i, pp->p_size, pp->p_offset);
1682 	}
1683 	return (errors);
1684 }
1685 
1686 #if NUMBOOT > 0
1687 /*
1688  * If we are installing a boot program that doesn't fit in d_bbsize
1689  * we need to mark those partitions that the boot overflows into.
1690  * This allows newfs to prevent creation of a filesystem where it might
1691  * clobber bootstrap code.
1692  */
1693 void
1694 setbootflag(lp)
1695 	struct disklabel *lp;
1696 {
1697 	struct partition *pp;
1698 	int i, errors = 0;
1699 	u_long boffset;
1700 	char part;
1701 
1702 	if (bootbuf == 0)
1703 		return;
1704 	boffset = bootsize / lp->d_secsize;
1705 	for (i = 0; i < lp->d_npartitions; i++) {
1706 		part = 'a' + i;
1707 		pp = &lp->d_partitions[i];
1708 		if (pp->p_size == 0)
1709 			continue;
1710 		if (boffset <= pp->p_offset) {
1711 			if (pp->p_fstype == FS_BOOT)
1712 				pp->p_fstype = FS_UNUSED;
1713 		} else if (pp->p_fstype != FS_BOOT) {
1714 			if (pp->p_fstype != FS_UNUSED) {
1715 				warnx("boot overlaps used partition %c", part);
1716 				errors++;
1717 			} else {
1718 				pp->p_fstype = FS_BOOT;
1719 				warnx("warning, boot overlaps partition %c, %s",
1720 				    part, "marked as FS_BOOT");
1721 			}
1722 		}
1723 	}
1724 	if (errors)
1725 		errx(4, "cannot install boot program");
1726 }
1727 #endif
1728 
1729 int
1730 cmplabel(lp1, lp2)
1731 	struct disklabel *lp1;
1732 	struct disklabel *lp2;
1733 {
1734 	struct disklabel lab1 = *lp1;
1735 	struct disklabel lab2 = *lp2;
1736 
1737 	/* We don't compare these fields */
1738 	lab1.d_magic = lab2.d_magic;
1739 	lab1.d_magic2 = lab2.d_magic2;
1740 	lab1.d_checksum = lab2.d_checksum;
1741 	lab1.d_bbsize = lab2.d_bbsize;
1742 	lab1.d_sbsize = lab2.d_sbsize;
1743 
1744 	return (memcmp(&lab1, &lab2, sizeof(struct disklabel)));
1745 }
1746 
1747 void
1748 usage()
1749 {
1750 	char *boot = "";
1751 	char blank[] = "                             ";
1752 
1753 #if NUMBOOT == 1
1754 	boot = " [-B [-b xxboot]]";
1755 #elif NUMBOOT == 2
1756 	boot = " [-B [-b xxboot [-s bootxx]]]";
1757 #endif
1758 	blank[strlen(boot)] = '\0';
1759 
1760 	fprintf(stderr, "usage:\n");
1761 	fprintf(stderr,
1762 	    "  disklabel [-nv] [-r|-cd] [-t] disk%s     (read)\n",
1763 	    blank);
1764 	fprintf(stderr,
1765 	    "  disklabel [-nv] [-r|-cd] -e disk%s       (edit)\n",
1766 	    blank);
1767 	fprintf(stderr,
1768 	    "  disklabel [-nv] [-r|-cd] [-f temp] -E disk%.*s  (simple editor)\n",
1769 	    (int)strlen(blank) - 5, blank);
1770 	fprintf(stderr,
1771 	    "  disklabel [-nv] [-r]%s -R disk proto     (restore)\n",
1772 	    boot);
1773 	fprintf(stderr,
1774 	    "  disklabel [-nv] [-r]%s -w disk dtab [id] (write)\n",
1775 	    boot);
1776 	fprintf(stderr,
1777 	    "  disklabel [-nv] [-N|-W] disk%s           (protect)\n", blank);
1778 	fprintf(stderr,
1779 	    "`disk' may be of the forms: sd0 or /dev/rsd0%c.\n", 'a'+RAW_PART);
1780 	fprintf(stderr,
1781 	    "`dtab' is an entry from %s, see disktab(5) for more info.\n",
1782 	    DISKTAB);
1783 	fprintf(stderr,
1784 	    "`proto' is the output from the read cmd form; -R is powerful.\n");
1785 #ifdef SEEALSO
1786 	fprintf(stderr,
1787 	    "For procedures specific to this architecture see: %s\n", SEEALSO);
1788 #endif
1789 	exit(1);
1790 }
1791