xref: /386bsd/usr/src/sbin/disklabel/disklabel.c (revision a2142627)
1 /*
2  * Copyright (c) 1987 The Regents of the University of California.
3  * All rights reserved.
4  *
5  * This code is derived from software contributed to Berkeley by
6  * Symmetric Computer Systems.
7  *
8  * Redistribution and use in source and binary forms, with or without
9  * modification, are permitted provided that the following conditions
10  * are met:
11  * 1. Redistributions of source code must retain the above copyright
12  *    notice, this list of conditions and the following disclaimer.
13  * 2. Redistributions in binary form must reproduce the above copyright
14  *    notice, this list of conditions and the following disclaimer in the
15  *    documentation and/or other materials provided with the distribution.
16  * 3. All advertising materials mentioning features or use of this software
17  *    must display the following acknowledgement:
18  *	This product includes software developed by the University of
19  *	California, Berkeley and its contributors.
20  * 4. Neither the name of the University nor the names of its contributors
21  *    may be used to endorse or promote products derived from this software
22  *    without specific prior written permission.
23  *
24  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
25  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
26  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
27  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
28  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
29  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
30  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
31  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
32  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
33  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
34  * SUCH DAMAGE.
35  */
36 
37 #ifndef lint
38 char copyright[] =
39 "@(#) Copyright (c) 1987 The Regents of the University of California.\n\
40  All rights reserved.\n";
41 #endif /* not lint */
42 
43 #ifndef lint
44 static char sccsid[] = "@(#)disklabel.c	5.20 (Berkeley) 2/9/91";
45 /* from static char sccsid[] = "@(#)disklabel.c	1.2 (Symmetric) 11/28/85"; */
46 #endif /* not lint */
47 
48 #include <sys/param.h>
49 #include <sys/signal.h>
50 #include <sys/errno.h>
51 #include <sys/file.h>
52 #include <sys/ioctl.h>
53 #include <ufs/fs.h>
54 #include <string.h>
55 #define DKTYPENAMES
56 #include <sys/disklabel.h>
57 #include <stdio.h>
58 #include <unistd.h>
59 #include <ctype.h>
60 #include "pathnames.h"
61 
62 /*
63  * Disklabel: read and write disklabels.
64  * The label is usually placed on one of the first sectors of the disk.
65  * Many machines (VAX 11/750) also place a bootstrap in the same area,
66  * in which case the label is embedded in the bootstrap.
67  * The bootstrap source must leave space at the proper offset
68  * for the label on such machines.
69  *
70  * On 386BSD, the disklabel may either be at the start of the disk, or, at
71  * the start of an MS/DOS partition. In this way, it can be used either
72  * in concert with other operating systems sharing a disk, or with the
73  * disk dedicated to 386BSD. In shared mode, the DOS disk geometry must be
74  * identical to that which disklabel uses, and the disklabel must solely
75  * describe the space within the partition selected. Otherwise, the disk
76  * must be dedicated to 386BSD. -wfj
77  */
78 
79 #if defined(vax)
80 #define RAWPARTITION	'c'
81 #endif
82 
83 #if defined(i386)
84 /* with 386BSD, 'c' maps the portion of the disk given over to 386BSD,
85    and 'd' maps the entire drive, ignoring any partition tables */
86 #define RAWPARTITION	'd'
87 #endif
88 
89 #if defined(vax)==0 && defined(i386)==0
90 #define RAWPARTITION	'a'
91 #endif
92 
93 #ifndef BBSIZE
94 #define	BBSIZE	8192			/* size of boot area, with label */
95 #endif
96 
97 #if defined(vax) || defined(i386)
98 #define	BOOT				/* also have bootstrap in "boot area" */
99 #define	BOOTDIR	_PATH_BOOTDIR		/* source of boot binaries */
100 #else
101 #ifdef lint
102 #define	BOOT
103 #endif
104 #endif
105 
106 #define	DEFEDITOR	_PATH_VI
107 #define	streq(a,b)	(strcmp(a,b) == 0)
108 
109 #ifdef BOOT
110 char	*xxboot;
111 char	*bootxx;
112 #endif
113 
114 char	*dkname;
115 char	*specname;
116 char	tmpfil[] = _PATH_TMP;
117 
118 extern	int errno;
119 char	namebuf[BBSIZE], *np = namebuf;
120 struct	disklabel lab;
121 struct	disklabel *readlabel(), *makebootarea();
122 char	bootarea[BBSIZE];
123 char	boot0[MAXPATHLEN];
124 char	boot1[MAXPATHLEN];
125 
126 enum	{ UNSPEC, EDIT, NOWRITE, READ, RESTORE, WRITE, WRITEABLE } op = UNSPEC;
127 
128 int	rflag;
129 
130 #ifdef DEBUG
131 int	debug;
132 #endif
133 
134 #ifdef __386BSD__
135 struct dos_partition *dosdp;	/* 386BSD DOS partition, if found */
136 struct dos_partition *readmbr(int);
137 #endif
138 
main(argc,argv)139 main(argc, argv)
140 	int argc;
141 	char *argv[];
142 {
143 	extern int optind;
144 	register struct disklabel *lp;
145 	FILE *t;
146 	int ch, f, error = 0;
147 	char *name = 0, *type;
148 	off_t lseek(int, off_t, int);
149 
150 	while ((ch = getopt(argc, argv, "NRWerw")) != EOF)
151 		switch (ch) {
152 			case 'N':
153 				if (op != UNSPEC)
154 					usage();
155 				op = NOWRITE;
156 				break;
157 			case 'R':
158 				if (op != UNSPEC)
159 					usage();
160 				op = RESTORE;
161 				break;
162 			case 'W':
163 				if (op != UNSPEC)
164 					usage();
165 				op = WRITEABLE;
166 				break;
167 			case 'e':
168 				if (op != UNSPEC)
169 					usage();
170 				op = EDIT;
171 				break;
172 			case 'r':
173 				++rflag;
174 				break;
175 			case 'w':
176 				if (op != UNSPEC)
177 					usage();
178 				op = WRITE;
179 				break;
180 #ifdef DEBUG
181 			case 'd':
182 				debug++;
183 				break;
184 #endif
185 			case '?':
186 			default:
187 				usage();
188 		}
189 	argc -= optind;
190 	argv += optind;
191 	if (op == UNSPEC)
192 		op = READ;
193 	if (argc < 1)
194 		usage();
195 
196 	dkname = argv[0];
197 	if (dkname[0] != '/') {
198 		(void)sprintf(np, "%sr%s%c", _PATH_DEV, dkname, RAWPARTITION);
199 		specname = np;
200 		np += strlen(specname) + 1;
201 	} else
202 		specname = dkname;
203 	f = open(specname, op == READ ? O_RDONLY : O_RDWR);
204 	if (f < 0 && errno == ENOENT && dkname[0] != '/') {
205 		(void)sprintf(specname, "%sr%s", _PATH_DEV, dkname);
206 		np = namebuf + strlen(specname) + 1;
207 		f = open(specname, op == READ ? O_RDONLY : O_RDWR);
208 	}
209 	if (f < 0)
210 		Perror(specname);
211 
212 #ifdef	__386BSD__
213 	/*
214 	 * Check for presence of DOS partition table in
215 	 * master boot record. Return pointer to 386BSD
216 	 * partition, if present. If no valid partition table,
217 	 * return 0. If valid partition table present, but no
218 	 * partition to use, return a pointer to a non-386bsd
219 	 * partition.
220 	 */
221 	dosdp = readmbr(f);
222 	{ int mfd; unsigned char params[0x10];
223 		/* sleezy, but we need it fast! */
224 		mfd = open("/dev/mem", 0);
225 		(void)lseek(mfd, (off_t)0x300, L_SET);
226 		read (mfd, params, 0x10);
227 	}
228 
229 #endif
230 
231 	switch(op) {
232 	case EDIT:
233 		if (argc != 1)
234 			usage();
235 		lp = readlabel(f);
236 		error = edit(lp, f);
237 		break;
238 	case NOWRITE: {
239 		int flag = 0;
240 		if (ioctl(f, DIOCWLABEL, (char *)&flag) < 0)
241 			Perror("ioctl DIOCWLABEL");
242 		break;
243 	}
244 	case READ:
245 		if (argc != 1)
246 			usage();
247 
248 		lp = readlabel(f);
249 		display(stdout, lp);
250 		error = checklabel(lp);
251 		break;
252 	case RESTORE:
253 #ifdef BOOT
254 		if (rflag) {
255 			if (argc == 4) {	/* [ priboot secboot ] */
256 				xxboot = argv[2];
257 				bootxx = argv[3];
258 				lab.d_secsize = DEV_BSIZE;	/* XXX */
259 				lab.d_bbsize = BBSIZE;		/* XXX */
260 			}
261 			else if (argc == 3) 	/* [ disktype ] */
262 				makelabel(argv[2], (char *)NULL, &lab);
263 			else {
264 				fprintf(stderr,
265 "Must specify either disktype or bootfiles with -r flag of RESTORE option\n");
266 				exit(1);
267 			}
268 		}
269 		else
270 #endif
271 		if (argc != 2)
272 			usage();
273 		lp = makebootarea(bootarea, &lab);
274 		if (!(t = fopen(argv[1],"r")))
275 			Perror(argv[1]);
276 		if (getasciilabel(t, lp))
277 			error = writelabel(f, bootarea, lp);
278 		break;
279 	case WRITE:
280 		type = argv[1];
281 #ifdef BOOT
282 		if (argc > 5 || argc < 2)
283 			usage();
284 		if (argc > 3) {
285 			bootxx = argv[--argc];
286 			xxboot = argv[--argc];
287 		}
288 #else
289 		if (argc > 3 || argc < 2)
290 			usage();
291 #endif
292 		if (argc > 2)
293 			name = argv[--argc];
294 		makelabel(type, name, &lab);
295 		lp = makebootarea(bootarea, &lab);
296 		*lp = lab;
297 		if (checklabel(lp) == 0)
298 			error = writelabel(f, bootarea, lp);
299 		break;
300 	case WRITEABLE: {
301 		int flag = 1;
302 		if (ioctl(f, DIOCWLABEL, (char *)&flag) < 0)
303 			Perror("ioctl DIOCWLABEL");
304 		break;
305 	}
306 	}
307 	exit(error);
308 }
309 
310 /*
311  * Construct a prototype disklabel from /etc/disktab.  As a side
312  * effect, set the names of the primary and secondary boot files
313  * if specified.
314  */
makelabel(type,name,lp)315 makelabel(type, name, lp)
316 	char *type, *name;
317 	register struct disklabel *lp;
318 {
319 	register struct disklabel *dp;
320 	char *strcpy();
321 
322 	dp = getdiskbyname(type);
323 	if (dp == NULL) {
324 		fprintf(stderr, "%s: unknown disk type\n", type);
325 		exit(1);
326 	}
327 	*lp = *dp;
328 #ifdef BOOT
329 	/*
330 	 * Check if disktab specifies the bootstraps (b0 or b1).
331 	 */
332 	if (!xxboot && lp->d_boot0) {
333 		if (*lp->d_boot0 != '/')
334 			(void)sprintf(boot0, "%s/%s", BOOTDIR, lp->d_boot0);
335 		else
336 			(void)strcpy(boot0, lp->d_boot0);
337 		xxboot = boot0;
338 	}
339 	if (!bootxx && lp->d_boot1) {
340 		if (*lp->d_boot1 != '/')
341 			(void)sprintf(boot1, "%s/%s", BOOTDIR, lp->d_boot1);
342 		else
343 			(void)strcpy(boot1, lp->d_boot1);
344 		bootxx = boot1;
345 	}
346 	/*
347 	 * If bootstraps not specified anywhere, makebootarea()
348 	 * will choose ones based on the name of the disk special
349 	 * file. E.g. /dev/ra0 -> raboot, bootra
350 	 */
351 #endif /*BOOT*/
352 	/* d_packname is union d_boot[01], so zero */
353 	bzero(lp->d_packname, sizeof(lp->d_packname));
354 	if (name)
355 		(void)strncpy(lp->d_packname, name, sizeof(lp->d_packname));
356 }
357 
writelabel(f,boot,lp)358 writelabel(f, boot, lp)
359 	int f;
360 	char *boot;
361 	register struct disklabel *lp;
362 {
363 	register int i;
364 	int flag;
365 	off_t lseek();
366 #ifdef	__386BSD__
367 	off_t lbl_off; struct partition *pp = lp->d_partitions;
368 #endif
369 
370 	lp->d_magic = DISKMAGIC;
371 	lp->d_magic2 = DISKMAGIC;
372 	lp->d_checksum = 0;
373 	lp->d_checksum = dkcksum(lp);
374 	if (rflag) {
375 
376 #ifdef	__386BSD__
377 		/*
378 		 * If 386BSD DOS partition is missing, or if
379 		 * the label to be written is not within partition,
380 		 * prompt first. Need to allow this in case operator
381 		 * wants to convert the drive for dedicated use.
382 		 * In this case, partition 'a' had better start at 0,
383 		 * otherwise we reject the request as meaningless. -wfj
384 		 */
385 
386 		if (dosdp && dosdp->dp_typ == DOSPTYP_386BSD && pp->p_size &&
387 			dosdp->dp_start == pp->p_offset) {
388 			lbl_off = pp->p_offset;
389 		} else {
390 			if (dosdp) {
391 				char c;
392 
393 				printf("overwriting disk with DOS partition table? (n):");
394 				fflush(stdout);
395 				c = getchar();
396 				if (c != EOF && c != (int)'\n')
397 					while (getchar() != (int)'\n')
398 						;
399 				if  (c == (int)'n')
400 					exit(0);
401 			}
402 			lbl_off = 0;
403 		}
404 		(void)lseek(f, (off_t)(lbl_off * lp->d_secsize), L_SET);
405 #endif
406 		/*
407 		 * First set the kernel disk label,
408 		 * then write a label to the raw disk.
409 		 * If the SDINFO ioctl fails because it is unimplemented,
410 		 * keep going; otherwise, the kernel consistency checks
411 		 * may prevent us from changing the current (in-core)
412 		 * label.
413 		 */
414 		if (ioctl(f, DIOCSDINFO, lp) < 0 &&
415 		    errno != ENODEV && errno != ENOTTY) {
416 			l_perror("ioctl DIOCSDINFO");
417 			/*return (1);*/
418 		}
419 
420 		/*
421 		 * write enable label sector before write (if necessary),
422 		 * disable after writing.
423 		 */
424 		flag = 1;
425 		if (ioctl(f, DIOCWLABEL, &flag) < 0)
426 			perror("ioctl DIOCWLABEL");
427 		if (write(f, boot, lp->d_bbsize) != lp->d_bbsize) {
428 			perror("write");
429 			return (1);
430 		}
431 		flag = 0;
432 		(void) ioctl(f, DIOCWLABEL, &flag);
433 	} else if (ioctl(f, DIOCWDINFO, lp) < 0) {
434 		l_perror("ioctl DIOCWDINFO");
435 		return (1);
436 	}
437 
438 	if (lp->d_type != DTYPE_SCSI && lp->d_flags & D_BADSECT) {
439 		daddr_t alt;
440 
441 		alt = lp->d_ncylinders * lp->d_secpercyl - lp->d_nsectors;
442 		for (i = 1; i < 11 && i < lp->d_nsectors; i += 2) {
443 			(void)lseek(f, (off_t)((alt + i) * lp->d_secsize), L_SET);
444 			if (write(f, boot, lp->d_secsize) < lp->d_secsize) {
445 				int oerrno = errno;
446 				fprintf(stderr, "alternate label %d ", i/2);
447 				errno = oerrno;
448 				perror("write");
449 			}
450 		}
451 	}
452 
453 	return (0);
454 }
455 
l_perror(s)456 l_perror(s)
457 	char *s;
458 {
459 	int saverrno = errno;
460 
461 	fprintf(stderr, "disklabel: %s: ", s);
462 
463 	switch (saverrno) {
464 
465 	case ESRCH:
466 		fprintf(stderr, "No disk label on disk;\n");
467 		fprintf(stderr,
468 		    "use \"disklabel -r\" to install initial label\n");
469 		break;
470 
471 	case EINVAL:
472 		fprintf(stderr, "Label magic number or checksum is wrong!\n");
473 		fprintf(stderr, "(disklabel or kernel is out of date?)\n");
474 		break;
475 
476 	case EBUSY:
477 		fprintf(stderr, "Open partition would move or shrink\n");
478 		break;
479 
480 	case EXDEV:
481 		fprintf(stderr,
482 	"Labeled partition or 'a' partition must start at beginning of disk\n");
483 		fprintf(stderr, "or DOS partition\n");
484 		break;
485 
486 	default:
487 		errno = saverrno;
488 		perror((char *)NULL);
489 		break;
490 	}
491 }
492 
493 #ifdef __386BSD__
494 /*
495  * Fetch DOS partition table from disk.
496  */
497 struct dos_partition *
readmbr(f)498 readmbr(f)
499 	int f;
500 {
501 	static struct dos_partition dos_partitions[NDOSPART];
502 	struct dos_partition *dp, *bsdp;
503 	char mbr[DEV_BSIZE];
504 	int i, npart, nboot, njunk;
505 
506 	(void)lseek(f, (off_t)DOSBBSECTOR, L_SET);
507 	if (read(f, mbr, sizeof(mbr)) < sizeof(mbr))
508 		Perror("can't read master boot record");
509 
510 	bcopy(mbr + DOSPARTOFF, dos_partitions, sizeof(dos_partitions));
511 
512 	/*
513 	 * Don't (yet) know disk geometry (BIOS), use
514 	 * partition table to find 386BSD partition, and obtain
515 	 * disklabel from there.
516 	 */
517 	dp = dos_partitions;
518 	npart = njunk = nboot = 0;
519 	for (i = 0; i < NDOSPART; i++, dp++) {
520 		if (dp->dp_flag != 0x80 && dp->dp_flag != 0) njunk++;
521 		else
522 			if (dp->dp_size > 0) npart++;
523 		if (dp->dp_flag == 0x80) nboot++;
524 		if (dp->dp_size && dp->dp_typ == DOSPTYP_386BSD)
525 			bsdp = dp;
526 	}
527 
528 	/* valid partition table? */
529 	if (nboot <= 1 || npart == 0 || njunk)
530 		return (0);
531 	/* if no bsd partition, pass back first one */
532 	if (!bsdp) {
533 		Warning("DOS partition table with no valid 386BSD partition");
534 		return (dos_partitions);
535 	}
536 	return (bsdp);
537 }
538 #endif
539 
540 /*
541  * Fetch disklabel for disk.
542  * Use ioctl to get label unless -r flag is given.
543  */
544 struct disklabel *
readlabel(f)545 readlabel(f)
546 	int f;
547 {
548 	register struct disklabel *lp;
549 
550 	if (rflag) {
551 #ifdef __386BSD__
552 		off_t sectoffset;
553 
554 		if (dosdp && dosdp->dp_size && dosdp->dp_typ == DOSPTYP_386BSD)
555 			sectoffset = dosdp->dp_start * DEV_BSIZE;
556 		else
557 			sectoffset = 0;
558 		(void)lseek(f, sectoffset, L_SET);
559 #endif
560 
561 		if (read(f, bootarea, BBSIZE) < BBSIZE)
562 			Perror(specname);
563 		for (lp = (struct disklabel *)bootarea;
564 		    lp <= (struct disklabel *)(bootarea + BBSIZE - sizeof(*lp));
565 		    lp = (struct disklabel *)((char *)lp + 16))
566 			if (lp->d_magic == DISKMAGIC &&
567 			    lp->d_magic2 == DISKMAGIC)
568 				break;
569 		if (lp > (struct disklabel *)(bootarea+BBSIZE-sizeof(*lp)) ||
570 		    lp->d_magic != DISKMAGIC || lp->d_magic2 != DISKMAGIC ||
571 		    dkcksum(lp) != 0) {
572 			fprintf(stderr,
573 	"Bad pack magic number (label is damaged, or pack is unlabeled)\n");
574 			/* lp = (struct disklabel *)(bootarea + LABELOFFSET);
575 			exit (1); */
576 			goto tryioctl;
577 		}
578 	} else {
579 tryioctl:
580 		lp = &lab;
581 		if (ioctl(f, DIOCGDINFO, lp) < 0)
582 			Perror("ioctl DIOCGDINFO");
583 	}
584 	return (lp);
585 }
586 
587 struct disklabel *
makebootarea(boot,dp)588 makebootarea(boot, dp)
589 	char *boot;
590 	register struct disklabel *dp;
591 {
592 	struct disklabel *lp;
593 	register char *p;
594 	int b;
595 #ifdef BOOT
596 	char	*dkbasename;
597 #endif /*BOOT*/
598 
599 	lp = (struct disklabel *)(boot + (LABELSECTOR * dp->d_secsize) +
600 	    LABELOFFSET);
601 #ifdef BOOT
602 	if (!rflag)
603 		return (lp);
604 
605 	if (xxboot == NULL || bootxx == NULL) {
606 		dkbasename = np;
607 		if ((p = rindex(dkname, '/')) == NULL)
608 			p = dkname;
609 		else
610 			p++;
611 		while (*p && !isdigit(*p))
612 			*np++ = *p++;
613 		*np++ = '\0';
614 
615 		if (xxboot == NULL) {
616 			(void)sprintf(np, "%s/%sboot", BOOTDIR, dkbasename);
617 			if (access(np, F_OK) < 0 && dkbasename[0] == 'r')
618 				dkbasename++;
619 			xxboot = np;
620 			(void)sprintf(xxboot, "%s/%sboot", BOOTDIR, dkbasename);
621 			np += strlen(xxboot) + 1;
622 		}
623 		if (bootxx == NULL) {
624 			(void)sprintf(np, "%s/boot%s", BOOTDIR, dkbasename);
625 			if (access(np, F_OK) < 0 && dkbasename[0] == 'r')
626 				dkbasename++;
627 			bootxx = np;
628 			(void)sprintf(bootxx, "%s/boot%s", BOOTDIR, dkbasename);
629 			np += strlen(bootxx) + 1;
630 		}
631 	}
632 #ifdef DEBUG
633 	if (debug)
634 		fprintf(stderr, "bootstraps: xxboot = %s, bootxx = %s\n",
635 			xxboot, bootxx);
636 #endif
637 
638 	b = open(xxboot, O_RDONLY);
639 	if (b < 0)
640 		Perror(xxboot);
641 	if (read(b, boot, (int)dp->d_secsize) < 0)
642 		Perror(xxboot);
643 	close(b);
644 	b = open(bootxx, O_RDONLY);
645 	if (b < 0)
646 		Perror(bootxx);
647 	if (read(b, &boot[dp->d_secsize], (int)(dp->d_bbsize-dp->d_secsize)) < 0)
648 		Perror(bootxx);
649 	(void)close(b);
650 #endif /*BOOT*/
651 
652 	for (p = (char *)lp; p < (char *)lp + sizeof(struct disklabel); p++)
653 		if (*p) {
654 			fprintf(stderr,
655 			    "Bootstrap doesn't leave room for disk label\n");
656 			exit(2);
657 		}
658 	return (lp);
659 }
660 
display(f,lp)661 display(f, lp)
662 	FILE *f;
663 	register struct disklabel *lp;
664 {
665 	register int i, j;
666 	register struct partition *pp;
667 
668 	fprintf(f, "# %s:\n", specname);
669 	if ((unsigned) lp->d_type < DKMAXTYPES)
670 		fprintf(f, "type: %s\n", dktypenames[lp->d_type]);
671 	else
672 		fprintf(f, "type: %d\n", lp->d_type);
673 	fprintf(f, "disk: %.*s\n", sizeof(lp->d_typename), lp->d_typename);
674 	fprintf(f, "label: %.*s\n", sizeof(lp->d_packname), lp->d_packname);
675 	fprintf(f, "flags:");
676 	if (lp->d_flags & D_REMOVABLE)
677 		fprintf(f, " removeable");
678 	if (lp->d_flags & D_ECC)
679 		fprintf(f, " ecc");
680 	if (lp->d_flags & D_BADSECT)
681 		fprintf(f, " badsect");
682 	fprintf(f, "\n");
683 	fprintf(f, "bytes/sector: %d\n", lp->d_secsize);
684 	fprintf(f, "sectors/track: %d\n", lp->d_nsectors);
685 	fprintf(f, "tracks/cylinder: %d\n", lp->d_ntracks);
686 	fprintf(f, "sectors/cylinder: %d\n", lp->d_secpercyl);
687 	fprintf(f, "cylinders: %d\n", lp->d_ncylinders);
688 	fprintf(f, "rpm: %d\n", lp->d_rpm);
689 	fprintf(f, "interleave: %d\n", lp->d_interleave);
690 	fprintf(f, "trackskew: %d\n", lp->d_trackskew);
691 	fprintf(f, "cylinderskew: %d\n", lp->d_cylskew);
692 	fprintf(f, "headswitch: %d\t\t# milliseconds\n", lp->d_headswitch);
693 	fprintf(f, "track-to-track seek: %d\t# milliseconds\n", lp->d_trkseek);
694 	fprintf(f, "drivedata: ");
695 	for (i = NDDATA - 1; i >= 0; i--)
696 		if (lp->d_drivedata[i])
697 			break;
698 	if (i < 0)
699 		i = 0;
700 	for (j = 0; j <= i; j++)
701 		fprintf(f, "%d ", lp->d_drivedata[j]);
702 	fprintf(f, "\n\n%d partitions:\n", lp->d_npartitions);
703 	fprintf(f,
704 	    "#        size   offset    fstype   [fsize bsize   cpg]\n");
705 	pp = lp->d_partitions;
706 	for (i = 0; i < lp->d_npartitions; i++, pp++) {
707 		if (pp->p_size) {
708 			fprintf(f, "  %c: %8d %8d  ", 'a' + i,
709 			   pp->p_size, pp->p_offset);
710 			if ((unsigned) pp->p_fstype < FSMAXTYPES)
711 				fprintf(f, "%8.8s", fstypenames[pp->p_fstype]);
712 			else
713 				fprintf(f, "%8d", pp->p_fstype);
714 			switch (pp->p_fstype) {
715 
716 			case FS_UNUSED:				/* XXX */
717 				fprintf(f, "    %5d %5d %5.5s ",
718 				    pp->p_fsize, pp->p_fsize * pp->p_frag, "");
719 				break;
720 
721 			case FS_BSDFFS:
722 				fprintf(f, "    %5d %5d %5d ",
723 				    pp->p_fsize, pp->p_fsize * pp->p_frag,
724 				    pp->p_cpg);
725 				break;
726 
727 			default:
728 				fprintf(f, "%20.20s", "");
729 				break;
730 			}
731 			fprintf(f, "\t# (Cyl. %4d",
732 			    pp->p_offset / lp->d_secpercyl);
733 			if (pp->p_offset % lp->d_secpercyl)
734 			    putc('*', f);
735 			else
736 			    putc(' ', f);
737 			fprintf(f, "- %d",
738 			    (pp->p_offset +
739 			    pp->p_size + lp->d_secpercyl - 1) /
740 			    lp->d_secpercyl - 1);
741 			if (pp->p_size % lp->d_secpercyl)
742 			    putc('*', f);
743 			fprintf(f, ")\n");
744 		}
745 	}
746 	fflush(f);
747 }
748 
749 edit(lp, f)
750 	struct disklabel *lp;
751 	int f;
752 {
753 	register int c;
754 	struct disklabel label;
755 	FILE *fd;
756 	char *mktemp();
757 
758 	(void) mktemp(tmpfil);
759 	fd = fopen(tmpfil, "w");
760 	if (fd == NULL) {
761 		fprintf(stderr, "%s: Can't create\n", tmpfil);
762 		return (1);
763 	}
764 	(void)fchmod(fd, 0600);
765 	display(fd, lp);
766 	fclose(fd);
767 	for (;;) {
768 		if (!editit())
769 			break;
770 		fd = fopen(tmpfil, "r");
771 		if (fd == NULL) {
772 			fprintf(stderr, "%s: Can't reopen for reading\n",
773 				tmpfil);
774 			break;
775 		}
776 		bzero((char *)&label, sizeof(label));
777 		if (getasciilabel(fd, &label)) {
778 			*lp = label;
779 			if (writelabel(f, bootarea, lp) == 0) {
780 				(void) unlink(tmpfil);
781 				return (0);
782 			}
783 		}
784 		printf("re-edit the label? [y]: "); fflush(stdout);
785 		c = getchar();
786 		if (c != EOF && c != (int)'\n')
787 			while (getchar() != (int)'\n')
788 				;
789 		if  (c == (int)'n')
790 			break;
791 	}
792 	(void) unlink(tmpfil);
793 	return (1);
794 }
795 
editit()796 editit()
797 {
798 	register int pid, xpid;
799 	int stat, omask;
800 	extern char *getenv();
801 
802 	omask = sigblock(sigmask(SIGINT)|sigmask(SIGQUIT)|sigmask(SIGHUP));
803 	while ((pid = fork()) < 0) {
804 		extern int errno;
805 
806 		if (errno == EPROCLIM) {
807 			fprintf(stderr, "You have too many processes\n");
808 			return(0);
809 		}
810 		if (errno != EAGAIN) {
811 			perror("fork");
812 			return(0);
813 		}
814 		sleep(1);
815 	}
816 	if (pid == 0) {
817 		register char *ed;
818 
819 		sigsetmask(omask);
820 		setgid(getgid());
821 		setuid(getuid());
822 		if ((ed = getenv("EDITOR")) == (char *)0)
823 			ed = DEFEDITOR;
824 		execlp(ed, ed, tmpfil, 0);
825 		perror(ed);
826 		exit(1);
827 	}
828 	while ((xpid = wait(&stat)) >= 0)
829 		if (xpid == pid)
830 			break;
831 	sigsetmask(omask);
832 	return(!stat);
833 }
834 
835 char *
skip(cp)836 skip(cp)
837 	register char *cp;
838 {
839 
840 	while (*cp != '\0' && isspace(*cp))
841 		cp++;
842 	if (*cp == '\0' || *cp == '#')
843 		return ((char *)NULL);
844 	return (cp);
845 }
846 
847 char *
word(cp)848 word(cp)
849 	register char *cp;
850 {
851 	register char c;
852 
853 	while (*cp != '\0' && !isspace(*cp) && *cp != '#')
854 		cp++;
855 	if ((c = *cp) != '\0') {
856 		*cp++ = '\0';
857 		if (c != '#')
858 			return (skip(cp));
859 	}
860 	return ((char *)NULL);
861 }
862 
863 /*
864  * Read an ascii label in from fd f,
865  * in the same format as that put out by display(),
866  * and fill in lp.
867  */
getasciilabel(f,lp)868 getasciilabel(f, lp)
869 	FILE	*f;
870 	register struct disklabel *lp;
871 {
872 	register char **cpp, *cp;
873 	register struct partition *pp;
874 	char *tp, *s, line[BUFSIZ];
875 	int v, lineno = 0, errors = 0;
876 
877 	lp->d_bbsize = BBSIZE;				/* XXX */
878 	lp->d_sbsize = SBSIZE;				/* XXX */
879 	while (fgets(line, sizeof(line) - 1, f)) {
880 		lineno++;
881 		if (cp = index(line,'\n'))
882 			*cp = '\0';
883 		cp = skip(line);
884 		if (cp == NULL)
885 			continue;
886 		tp = index(cp, ':');
887 		if (tp == NULL) {
888 			fprintf(stderr, "line %d: syntax error\n", lineno);
889 			errors++;
890 			continue;
891 		}
892 		*tp++ = '\0', tp = skip(tp);
893 		if (streq(cp, "type")) {
894 			if (tp == NULL)
895 				tp = "unknown";
896 			cpp = dktypenames;
897 			for (; cpp < &dktypenames[DKMAXTYPES]; cpp++)
898 				if ((s = *cpp) && streq(s, tp)) {
899 					lp->d_type = cpp - dktypenames;
900 					goto next;
901 				}
902 			v = atoi(tp);
903 			if ((unsigned)v >= DKMAXTYPES)
904 				fprintf(stderr, "line %d:%s %d\n", lineno,
905 				    "Warning, unknown disk type", v);
906 			lp->d_type = v;
907 			continue;
908 		}
909 		if (streq(cp, "flags")) {
910 			for (v = 0; (cp = tp) && *cp != '\0';) {
911 				tp = word(cp);
912 				if (streq(cp, "removeable"))
913 					v |= D_REMOVABLE;
914 				else if (streq(cp, "ecc"))
915 					v |= D_ECC;
916 				else if (streq(cp, "badsect"))
917 					v |= D_BADSECT;
918 				else {
919 					fprintf(stderr,
920 					    "line %d: %s: bad flag\n",
921 					    lineno, cp);
922 					errors++;
923 				}
924 			}
925 			lp->d_flags = v;
926 			continue;
927 		}
928 		if (streq(cp, "drivedata")) {
929 			register int i;
930 
931 			for (i = 0; (cp = tp) && *cp != '\0' && i < NDDATA;) {
932 				lp->d_drivedata[i++] = atoi(cp);
933 				tp = word(cp);
934 			}
935 			continue;
936 		}
937 		if (sscanf(cp, "%d partitions", &v) == 1) {
938 			if (v == 0 || (unsigned)v > MAXPARTITIONS) {
939 				fprintf(stderr,
940 				    "line %d: bad # of partitions\n", lineno);
941 				lp->d_npartitions = MAXPARTITIONS;
942 				errors++;
943 			} else
944 				lp->d_npartitions = v;
945 			continue;
946 		}
947 		if (tp == NULL)
948 			tp = "";
949 		if (streq(cp, "disk")) {
950 			strncpy(lp->d_typename, tp, sizeof (lp->d_typename));
951 			continue;
952 		}
953 		if (streq(cp, "label")) {
954 			strncpy(lp->d_packname, tp, sizeof (lp->d_packname));
955 			continue;
956 		}
957 		if (streq(cp, "bytes/sector")) {
958 			v = atoi(tp);
959 			if (v <= 0 || (v % 512) != 0) {
960 				fprintf(stderr,
961 				    "line %d: %s: bad sector size\n",
962 				    lineno, tp);
963 				errors++;
964 			} else
965 				lp->d_secsize = v;
966 			continue;
967 		}
968 		if (streq(cp, "sectors/track")) {
969 			v = atoi(tp);
970 			if (v <= 0) {
971 				fprintf(stderr, "line %d: %s: bad %s\n",
972 				    lineno, tp, cp);
973 				errors++;
974 			} else
975 				lp->d_nsectors = v;
976 			continue;
977 		}
978 		if (streq(cp, "sectors/cylinder")) {
979 			v = atoi(tp);
980 			if (v <= 0) {
981 				fprintf(stderr, "line %d: %s: bad %s\n",
982 				    lineno, tp, cp);
983 				errors++;
984 			} else
985 				lp->d_secpercyl = v;
986 			continue;
987 		}
988 		if (streq(cp, "tracks/cylinder")) {
989 			v = atoi(tp);
990 			if (v <= 0) {
991 				fprintf(stderr, "line %d: %s: bad %s\n",
992 				    lineno, tp, cp);
993 				errors++;
994 			} else
995 				lp->d_ntracks = v;
996 			continue;
997 		}
998 		if (streq(cp, "cylinders")) {
999 			v = atoi(tp);
1000 			if (v <= 0) {
1001 				fprintf(stderr, "line %d: %s: bad %s\n",
1002 				    lineno, tp, cp);
1003 				errors++;
1004 			} else
1005 				lp->d_ncylinders = v;
1006 			continue;
1007 		}
1008 		if (streq(cp, "rpm")) {
1009 			v = atoi(tp);
1010 			if (v <= 0) {
1011 				fprintf(stderr, "line %d: %s: bad %s\n",
1012 				    lineno, tp, cp);
1013 				errors++;
1014 			} else
1015 				lp->d_rpm = v;
1016 			continue;
1017 		}
1018 		if (streq(cp, "interleave")) {
1019 			v = atoi(tp);
1020 			if (v <= 0) {
1021 				fprintf(stderr, "line %d: %s: bad %s\n",
1022 				    lineno, tp, cp);
1023 				errors++;
1024 			} else
1025 				lp->d_interleave = v;
1026 			continue;
1027 		}
1028 		if (streq(cp, "trackskew")) {
1029 			v = atoi(tp);
1030 			if (v < 0) {
1031 				fprintf(stderr, "line %d: %s: bad %s\n",
1032 				    lineno, tp, cp);
1033 				errors++;
1034 			} else
1035 				lp->d_trackskew = v;
1036 			continue;
1037 		}
1038 		if (streq(cp, "cylinderskew")) {
1039 			v = atoi(tp);
1040 			if (v < 0) {
1041 				fprintf(stderr, "line %d: %s: bad %s\n",
1042 				    lineno, tp, cp);
1043 				errors++;
1044 			} else
1045 				lp->d_cylskew = v;
1046 			continue;
1047 		}
1048 		if (streq(cp, "headswitch")) {
1049 			v = atoi(tp);
1050 			if (v < 0) {
1051 				fprintf(stderr, "line %d: %s: bad %s\n",
1052 				    lineno, tp, cp);
1053 				errors++;
1054 			} else
1055 				lp->d_headswitch = v;
1056 			continue;
1057 		}
1058 		if (streq(cp, "track-to-track seek")) {
1059 			v = atoi(tp);
1060 			if (v < 0) {
1061 				fprintf(stderr, "line %d: %s: bad %s\n",
1062 				    lineno, tp, cp);
1063 				errors++;
1064 			} else
1065 				lp->d_trkseek = v;
1066 			continue;
1067 		}
1068 		if ('a' <= *cp && *cp <= 'z' && cp[1] == '\0') {
1069 			unsigned part = *cp - 'a';
1070 
1071 			if (part > lp->d_npartitions) {
1072 				fprintf(stderr,
1073 				    "line %d: bad partition name\n", lineno);
1074 				errors++;
1075 				continue;
1076 			}
1077 			pp = &lp->d_partitions[part];
1078 #define NXTNUM(n) { \
1079 	cp = tp, tp = word(cp); \
1080 	if (tp == NULL) \
1081 		tp = cp; \
1082 	(n) = atoi(cp); \
1083      }
1084 
1085 			NXTNUM(v);
1086 			if (v < 0) {
1087 				fprintf(stderr,
1088 				    "line %d: %s: bad partition size\n",
1089 				    lineno, cp);
1090 				errors++;
1091 			} else
1092 				pp->p_size = v;
1093 			NXTNUM(v);
1094 			if (v < 0) {
1095 				fprintf(stderr,
1096 				    "line %d: %s: bad partition offset\n",
1097 				    lineno, cp);
1098 				errors++;
1099 			} else
1100 				pp->p_offset = v;
1101 			cp = tp, tp = word(cp);
1102 			cpp = fstypenames;
1103 			for (; cpp < &fstypenames[FSMAXTYPES]; cpp++)
1104 				if ((s = *cpp) && streq(s, cp)) {
1105 					pp->p_fstype = cpp - fstypenames;
1106 					goto gottype;
1107 				}
1108 			if (isdigit(*cp))
1109 				v = atoi(cp);
1110 			else
1111 				v = FSMAXTYPES;
1112 			if ((unsigned)v >= FSMAXTYPES) {
1113 				fprintf(stderr, "line %d: %s %s\n", lineno,
1114 				    "Warning, unknown filesystem type", cp);
1115 				v = FS_UNUSED;
1116 			}
1117 			pp->p_fstype = v;
1118 	gottype:
1119 
1120 			switch (pp->p_fstype) {
1121 
1122 			case FS_UNUSED:				/* XXX */
1123 				NXTNUM(pp->p_fsize);
1124 				if (pp->p_fsize == 0)
1125 					break;
1126 				NXTNUM(v);
1127 				pp->p_frag = v / pp->p_fsize;
1128 				break;
1129 
1130 			case FS_BSDFFS:
1131 				NXTNUM(pp->p_fsize);
1132 				if (pp->p_fsize == 0)
1133 					break;
1134 				NXTNUM(v);
1135 				pp->p_frag = v / pp->p_fsize;
1136 				NXTNUM(pp->p_cpg);
1137 				break;
1138 
1139 			default:
1140 				break;
1141 			}
1142 			continue;
1143 		}
1144 		fprintf(stderr, "line %d: %s: Unknown disklabel field\n",
1145 		    lineno, cp);
1146 		errors++;
1147 	next:
1148 		;
1149 	}
1150 	errors += checklabel(lp);
1151 	return (errors == 0);
1152 }
1153 
1154 /*
1155  * Check disklabel for errors and fill in
1156  * derived fields according to supplied values.
1157  */
checklabel(lp)1158 checklabel(lp)
1159 	register struct disklabel *lp;
1160 {
1161 	register struct partition *pp;
1162 	int i, errors = 0;
1163 	char part;
1164 
1165 	if (lp->d_secsize == 0) {
1166 		fprintf(stderr, "sector size %d\n", lp->d_secsize);
1167 		return (1);
1168 	}
1169 	if (lp->d_nsectors == 0) {
1170 		fprintf(stderr, "sectors/track %d\n", lp->d_nsectors);
1171 		return (1);
1172 	}
1173 	if (lp->d_ntracks == 0) {
1174 		fprintf(stderr, "tracks/cylinder %d\n", lp->d_ntracks);
1175 		return (1);
1176 	}
1177 	if  (lp->d_ncylinders == 0) {
1178 		fprintf(stderr, "cylinders/unit %d\n", lp->d_ncylinders);
1179 		errors++;
1180 	}
1181 	if (lp->d_rpm == 0)
1182 		Warning("revolutions/minute %d\n", lp->d_rpm);
1183 	if (lp->d_secpercyl == 0)
1184 		lp->d_secpercyl = lp->d_nsectors * lp->d_ntracks;
1185 	if (lp->d_secperunit == 0)
1186 		lp->d_secperunit = lp->d_secpercyl * lp->d_ncylinders;
1187 #ifdef __386BSD__notyet
1188 	if (dosdp && dosdp->dp_size && dosdp->dp_typ == DOSPTYP_386BSD
1189 		&& lp->d_secperunit > dosdp->dp_start + dosdp->dp_size) {
1190 		fprintf(stderr, "exceeds DOS partition size\n");
1191 		errors++;
1192 		lp->d_secperunit = dosdp->dp_start + dosdp->dp_size;
1193 	}
1194 	/* XXX should also check geometry against BIOS's idea */
1195 #endif
1196 	if (lp->d_bbsize == 0) {
1197 		fprintf(stderr, "boot block size %d\n", lp->d_bbsize);
1198 		errors++;
1199 	} else if (lp->d_bbsize % lp->d_secsize)
1200 		Warning("boot block size %% sector-size != 0\n");
1201 	if (lp->d_sbsize == 0) {
1202 		fprintf(stderr, "super block size %d\n", lp->d_sbsize);
1203 		errors++;
1204 	} else if (lp->d_sbsize % lp->d_secsize)
1205 		Warning("super block size %% sector-size != 0\n");
1206 	if (lp->d_npartitions > MAXPARTITIONS)
1207 		Warning("number of partitions (%d) > MAXPARTITIONS (%d)\n",
1208 		    lp->d_npartitions, MAXPARTITIONS);
1209 	for (i = 0; i < lp->d_npartitions; i++) {
1210 		part = 'a' + i;
1211 		pp = &lp->d_partitions[i];
1212 		if (pp->p_size == 0 && pp->p_offset != 0)
1213 			Warning("partition %c: size 0, but offset %d\n",
1214 			    part, pp->p_offset);
1215 #ifdef notdef
1216 		if (pp->p_size % lp->d_secpercyl)
1217 			Warning("partition %c: size %% cylinder-size != 0\n",
1218 			    part);
1219 		if (pp->p_offset % lp->d_secpercyl)
1220 			Warning("partition %c: offset %% cylinder-size != 0\n",
1221 			    part);
1222 #endif
1223 		if (pp->p_offset > lp->d_secperunit) {
1224 			fprintf(stderr,
1225 			    "partition %c: offset past end of unit\n", part);
1226 			errors++;
1227 		}
1228 		if (pp->p_offset + pp->p_size > lp->d_secperunit) {
1229 			fprintf(stderr,
1230 			    "partition %c: partition extends past end of unit\n",
1231 			    part);
1232 			errors++;
1233 		}
1234 	}
1235 	for (; i < MAXPARTITIONS; i++) {
1236 		part = 'a' + i;
1237 		pp = &lp->d_partitions[i];
1238 		if (pp->p_size || pp->p_offset)
1239 			Warning("unused partition %c: size %d offset %d\n",
1240 			    'a' + i, pp->p_size, pp->p_offset);
1241 	}
1242 	return (errors);
1243 }
1244 
1245 /*VARARGS1*/
Warning(fmt,a1,a2,a3,a4,a5)1246 Warning(fmt, a1, a2, a3, a4, a5)
1247 	char *fmt;
1248 {
1249 
1250 	fprintf(stderr, "Warning, ");
1251 	fprintf(stderr, fmt, a1, a2, a3, a4, a5);
1252 	fprintf(stderr, "\n");
1253 }
1254 
Perror(str)1255 Perror(str)
1256 	char *str;
1257 {
1258 	fputs("disklabel: ", stderr); perror(str);
1259 	exit(4);
1260 }
1261 
usage()1262 usage()
1263 {
1264 #ifdef BOOT
1265 	fprintf(stderr, "%-62s%s\n%-62s%s\n%-62s%s\n%-62s%s\n%-62s%s\n",
1266 "usage: disklabel [-r] disk", "(to read label)",
1267 "or disklabel -w [-r] disk type [ packid ] [ xxboot bootxx ]", "(to write label)",
1268 "or disklabel -e [-r] disk", "(to edit label)",
1269 "or disklabel -R [-r] disk protofile [ type | xxboot bootxx ]", "(to restore label)",
1270 "or disklabel [-NW] disk", "(to write disable/enable label)");
1271 #else
1272 	fprintf(stderr, "%-43s%s\n%-43s%s\n%-43s%s\n%-43s%s\n%-43s%s\n",
1273 "usage: disklabel [-r] disk", "(to read label)",
1274 "or disklabel -w [-r] disk type [ packid ]", "(to write label)",
1275 "or disklabel -e [-r] disk", "(to edit label)",
1276 "or disklabel -R [-r] disk protofile", "(to restore label)",
1277 "or disklabel [-NW] disk", "(to write disable/enable label)");
1278 #endif
1279 	exit(1);
1280 }
1281