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