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