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