xref: /netbsd-src/sbin/disklabel/main.c (revision d48f14661dda8638fee055ba15d35bdfb29b9fa8)
1 /*	$NetBSD: main.c,v 1.12 2006/06/25 21:32:39 christos Exp $	*/
2 
3 /*
4  * Copyright (c) 1987, 1993
5  *	The Regents of the University of California.  All rights reserved.
6  *
7  * This code is derived from software contributed to Berkeley by
8  * Symmetric Computer Systems.
9  *
10  * Redistribution and use in source and binary forms, with or without
11  * modification, are permitted provided that the following conditions
12  * are met:
13  * 1. Redistributions of source code must retain the above copyright
14  *    notice, this list of conditions and the following disclaimer.
15  * 2. Redistributions in binary form must reproduce the above copyright
16  *    notice, this list of conditions and the following disclaimer in the
17  *    documentation and/or other materials provided with the distribution.
18  * 3. Neither the name of the University nor the names of its contributors
19  *    may be used to endorse or promote products derived from this software
20  *    without specific prior written permission.
21  *
22  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
23  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
24  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
25  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
26  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
27  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
28  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
29  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
30  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
31  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
32  * SUCH DAMAGE.
33  */
34 
35 #if HAVE_NBTOOL_CONFIG_H
36 #include "nbtool_config.h"
37 #endif
38 
39 #include <sys/cdefs.h>
40 #ifndef lint
41 __COPYRIGHT("@(#) Copyright (c) 1987, 1993\n\
42 	The Regents of the University of California.  All rights reserved.\n");
43 #endif	/* not lint */
44 
45 #ifndef lint
46 #if 0
47 static char sccsid[] = "@(#)disklabel.c	8.4 (Berkeley) 5/4/95";
48 /* from static char sccsid[] = "@(#)disklabel.c	1.2 (Symmetric) 11/28/85"; */
49 #else
50 __RCSID("$NetBSD: main.c,v 1.12 2006/06/25 21:32:39 christos Exp $");
51 #endif
52 #endif	/* not lint */
53 
54 #include <sys/param.h>
55 #include <sys/file.h>
56 #include <sys/stat.h>
57 #include <sys/wait.h>
58 #define DKTYPENAMES
59 #define FSTYPENAMES
60 
61 #include <ctype.h>
62 #include <err.h>
63 #include <errno.h>
64 #include <unistd.h>
65 #include <signal.h>
66 #include <string.h>
67 #include <stdio.h>
68 #include <stdlib.h>
69 #include <unistd.h>
70 
71 #include <ufs/ufs/dinode.h>
72 #include <ufs/ffs/fs.h>
73 
74 #if HAVE_NBTOOL_CONFIG_H
75 #include <nbinclude/sys/disklabel.h>
76 #include <nbinclude/sys/disklabel_acorn.h>
77 #include <nbinclude/sys/bootblock.h>
78 #include "../../include/disktab.h"
79 #else
80 #include <sys/ioctl.h>
81 #include <sys/disklabel.h>
82 #include <sys/disklabel_acorn.h>
83 #include <sys/bootblock.h>
84 #include <util.h>
85 #include <disktab.h>
86 #endif /* HAVE_NBTOOL_CONFIG_H */
87 
88 #include "pathnames.h"
89 #include "extern.h"
90 #include "dkcksum.h"
91 
92 /*
93  * Disklabel: read and write disklabels.
94  * The label is usually placed on one of the first sectors of the disk.
95  * Many machines also place a bootstrap in the same area,
96  * in which case the label is embedded in the bootstrap.
97  * The bootstrap source must leave space at the proper offset
98  * for the label on such machines.
99  */
100 
101 #ifndef BBSIZE
102 #define	BBSIZE	8192			/* size of boot area, with label */
103 #endif
104 
105 #define DISKMAGIC_REV		bswap32(DISKMAGIC)
106 /* To delete a label, we just invert the magic numbers */
107 #define DISKMAGIC_DELETED	(~DISKMAGIC)
108 #define DISKMAGIC_DELETED_REV	bswap32(~DISKMAGIC)
109 
110 #define	DEFEDITOR	_PATH_VI
111 
112 char	specname[MAXPATHLEN];
113 
114 /* Some global data, all too hard to pass about */
115 int bootarea[BBSIZE / sizeof (int)];	/* Buffer matching part of disk */
116 int bootarea_len;			/* Number of bytes we actually read */
117 static struct	disklabel lab;		/* The label we have updated */
118 
119 static	int	Aflag;		/* Action all labels */
120 static	int	Fflag;		/* Read/write from file */
121 static	int	rflag;		/* Read/write direct from disk */
122 static	int	tflag;		/* Format output as disktab */
123 	int	Cflag;		/* CHS format output */
124 static	int	Dflag;		/* Delete old labels (use with write) */
125 static	int	Iflag;		/* Read/write direct, but default if absent */
126 static	int	mflag;		/* Expect disk to contain an MBR */
127 static int verbose;
128 static int read_all;		/* set if op = READ && Aflag */
129 
130 static int write_label(int);
131 static int readlabel_direct(int);
132 static void writelabel_direct(int);
133 static int update_label(int, u_int, u_int);
134 static struct disklabel *find_label(int, u_int);
135 
136 static void		 makedisktab(FILE *, struct disklabel *);
137 static void		 makelabel(const char *, const char *);
138 static void		 l_perror(const char *);
139 static void		 readlabel(int);
140 static int		 edit(int);
141 static int		 editit(const char *);
142 static char		*skip(char *);
143 static char		*word(char *);
144 static int		 getasciilabel(FILE *, struct disklabel *);
145 static void		 usage(void);
146 static int		 getulong(const char *, char, char **,
147     unsigned long *, unsigned long);
148 #define GETNUM32(a, v)	getulong(a, '\0', NULL, v, UINT32_MAX)
149 #define GETNUM16(a, v)	getulong(a, '\0', NULL, v, UINT16_MAX)
150 #define GETNUM8(a, v)	getulong(a, '\0', NULL, v, UINT8_MAX)
151 
152 static int set_writable_fd = -1;
153 
154 #if HAVE_NBTOOL_CONFIG_H
155 #define GETLABELOFFSET()	LABELOFFSET
156 #define GETLABELSECTOR()	LABELSECTOR
157 #else /* HAVE_NBTOOL_CONFIG_H */
158 #define GETLABELOFFSET()	getlabeloffset()
159 #define GETLABELSECTOR()	getlabelsector()
160 #endif
161 
162 /* Default location for label - only used if we don't find one to update */
163 #define LABEL_OFFSET (GETLABELSECTOR() * DEV_BSIZE + GETLABELOFFSET())
164 
165 /*
166  * For portability it doesn't make sense to use any other value....
167  * Except, maybe, the size of a physical sector.
168  * This value is used if we have to write a label to the start of an mbr ptn.
169  */
170 #ifndef	LABELOFFSET_MBR
171 #define	LABELOFFSET_MBR	512
172 #endif
173 
174 #if HAVE_NBTOOL_CONFIG_H
175 static int
176 opendisk(const char *path, int flags, char *buf, int buflen, int cooked)
177 {
178 	int f;
179 	f = open(path, flags, 0);
180 	strlcpy(buf, path, buflen);
181 	return f;
182 }
183 
184 static int
185 dk_ioctl(int f, void *arg)
186 {
187 	errno = ENOTTY;
188 	return -1;
189 }
190 #define dk_ioctl(f, cmd, arg) dk_ioctl(f, arg)
191 #else
192 #define dk_ioctl(f, cmd, arg) ioctl(f, cmd, arg)
193 #endif /* HAVE_NBTOOL_CONFIG_H */
194 
195 static void
196 clear_writable(void)
197 {
198 	static int zero = 0;
199 	dk_ioctl(set_writable_fd, DIOCWLABEL, &zero);
200 }
201 
202 int
203 main(int argc, char *argv[])
204 {
205 	FILE	*t;
206 	int	 ch, f, error;
207 	char	*dkname;
208 	struct stat sb;
209 	int	 writable;
210 	enum {
211 		UNSPEC, EDIT, READ, RESTORE, SETWRITABLE, SETREADONLY,
212 		WRITE, INTERACT, DELETE
213 	} op = UNSPEC, old_op;
214 
215 #ifdef USE_MBR
216 	mflag = 1;
217 #endif
218 #if HAVE_NBTOOL_CONFIG_H
219 	/* We must avoid doing any ioctl requests */
220 	Fflag = rflag = 1;
221 #endif
222 
223 	error = 0;
224 	while ((ch = getopt(argc, argv, "ABCDFINRWb:ef:imrs:tvw")) != -1) {
225 		old_op = op;
226 		switch (ch) {
227 		case 'A':	/* Action all labels */
228 			Aflag = 1;
229 			rflag = 1;
230 			break;
231 		case 'C':	/* Display in CHS format */
232 			Cflag = 1;
233 			break;
234 		case 'D':	/* Delete all existing labels */
235 			Dflag = 1;
236 			rflag = 1;
237 			break;
238 		case 'F':	/* Treat 'disk' as a regular file */
239 			Fflag = 1;
240 			rflag = 1;	/* Force direct access */
241 			break;
242 		case 'I':	/* Use default label if none found */
243 			Iflag = 1;
244 			rflag = 1;	/* Implies direct access */
245 			break;
246 		case 'R':	/* Restore label from text file */
247 			op = RESTORE;
248 			break;
249 		case 'N':	/* Disallow writes to label sector */
250 			op = SETREADONLY;
251 			break;
252 		case 'W':	/* Allow writes to label sector */
253 			op = SETWRITABLE;
254 			break;
255 		case 'e':	/* Edit label with $EDITOR */
256 			op = EDIT;
257 			break;
258 		case 'f':	/* Name of disktab file */
259 			if (setdisktab(optarg) == -1)
260 				usage();
261 			break;
262 		case 'i':	/* Edit using built-in editor */
263 			op = INTERACT;
264 			break;
265 		case 'm':	/* Expect disk to have an MBR */
266 			mflag ^= 1;
267 			break;
268 		case 'r':	/* Read/write label directly from disk */
269 			rflag = 1;
270 			break;
271 		case 't':	/* Format output as a disktab entry */
272 			tflag = 1;
273 			break;
274 		case 'v':	/* verbose/diag output */
275 			verbose++;
276 			break;
277 		case 'w':	/* Write label based on disktab entry */
278 			op = WRITE;
279 			break;
280 		case '?':
281 		default:
282 			usage();
283 		}
284 		if (old_op != UNSPEC && old_op != op)
285 			usage();
286 	}
287 	argc -= optind;
288 	argv += optind;
289 
290 	if (op == UNSPEC)
291 		op = Dflag ? DELETE : READ;
292 
293 	if (argc < 1)
294 		usage();
295 
296 	if (Iflag && op != EDIT && op != INTERACT)
297 		usage();
298 
299 	dkname = argv[0];
300 	f = opendisk(dkname, op == READ ? O_RDONLY : O_RDWR,
301 		    specname, sizeof specname, 0);
302 	if (f < 0)
303 		err(4, "%s", specname);
304 
305 	if (!Fflag && fstat(f, &sb) == 0 && S_ISREG(sb.st_mode))
306 		Fflag = rflag = 1;
307 
308 	switch (op) {
309 
310 	case DELETE:	/* Remove all existing labels */
311 		if (argc != 1)
312 			usage();
313 		Dflag = 2;
314 		writelabel_direct(f);
315 		break;
316 
317 	case EDIT:
318 		if (argc != 1)
319 			usage();
320 		readlabel(f);
321 		error = edit(f);
322 		break;
323 
324 	case INTERACT:
325 		if (argc != 1)
326 			usage();
327 		readlabel(f);
328 		/*
329 		 * XXX: Fill some default values so checklabel does not fail
330 		 */
331 		if (lab.d_bbsize == 0)
332 			lab.d_bbsize = BBSIZE;
333 		if (lab.d_sbsize == 0)
334 			lab.d_sbsize = SBLOCKSIZE;
335 		interact(&lab, f);
336 		break;
337 
338 	case READ:
339 		if (argc != 1)
340 			usage();
341 		read_all = Aflag;
342 		readlabel(f);
343 		if (read_all)
344 			/* Label got printed in the bowels of readlabel */
345 			break;
346 		if (tflag)
347 			makedisktab(stdout, &lab);
348 		else {
349 			showinfo(stdout, &lab, specname);
350 			showpartitions(stdout, &lab, Cflag);
351 		}
352 		error = checklabel(&lab);
353 		if (error)
354 			error += 100;
355 		break;
356 
357 	case RESTORE:
358 		if (argc != 2)
359 			usage();
360 		if (!(t = fopen(argv[1], "r")))
361 			err(4, "%s", argv[1]);
362 		if (getasciilabel(t, &lab))
363 			error = write_label(f);
364 		else
365 			error = 1;
366 		break;
367 
368 	case SETREADONLY:
369 		writable = 0;
370 		goto do_diocwlabel;
371 	case SETWRITABLE:
372 		writable = 1;
373 	    do_diocwlabel:
374 		if (argc != 1)
375 			usage();
376 		if (dk_ioctl(f, DIOCWLABEL, &writable) < 0)
377 			err(4, "ioctl DIOCWLABEL");
378 		break;
379 
380 	case WRITE:	/* Create label from /etc/disktab entry & write */
381 		if (argc < 2 || argc > 3)
382 			usage();
383 		makelabel(argv[1], argv[2]);
384 		if (checklabel(&lab) == 0)
385 			error = write_label(f);
386 		else
387 			error = 1;
388 		break;
389 
390 	case UNSPEC:
391 		usage();
392 
393 	}
394 	exit(error);
395 }
396 
397 /*
398  * Construct a prototype disklabel from /etc/disktab.
399  */
400 static void
401 makelabel(const char *type, const char *name)
402 {
403 	struct disklabel *dp;
404 
405 	dp = getdiskbyname(type);
406 	if (dp == NULL)
407 		errx(1, "unknown disk type: %s", type);
408 	lab = *dp;
409 
410 	/* d_packname is union d_boot[01], so zero */
411 	(void)memset(lab.d_packname, 0, sizeof(lab.d_packname));
412 	if (name)
413 		(void)strncpy(lab.d_packname, name, sizeof(lab.d_packname));
414 }
415 
416 static int
417 write_label(int f)
418 {
419 	int writable;
420 
421 	lab.d_magic = DISKMAGIC;
422 	lab.d_magic2 = DISKMAGIC;
423 	lab.d_checksum = 0;
424 	lab.d_checksum = dkcksum(&lab);
425 
426 	if (rflag) {
427 		/* Write the label directly to the disk */
428 
429 		/*
430 		 * First set the kernel disk label,
431 		 * then write a label to the raw disk.
432 		 * If the SDINFO ioctl fails because it is unimplemented,
433 		 * keep going; otherwise, the kernel consistency checks
434 		 * may prevent us from changing the current (in-core)
435 		 * label.
436 		 */
437 		if (!Fflag && dk_ioctl(f, DIOCSDINFO, &lab) < 0 &&
438 		    errno != ENODEV && errno != ENOTTY) {
439 			l_perror("ioctl DIOCSDINFO");
440 			return (1);
441 		}
442 		/*
443 		 * write enable label sector before write (if necessary),
444 		 * disable after writing.
445 		 */
446 		writable = 1;
447 		if (!Fflag) {
448 			if (dk_ioctl(f, DIOCWLABEL, &writable) < 0)
449 				perror("ioctl DIOCWLABEL");
450 			set_writable_fd = f;
451 			atexit(clear_writable);
452 		}
453 
454 		writelabel_direct(f);
455 
456 		/*
457 		 * Now issue a DIOCWDINFO. This will let the kernel convert the
458 		 * disklabel to some machdep format if needed.
459 		 */
460 		/* XXX: This is stupid! */
461 		if (!Fflag && dk_ioctl(f, DIOCWDINFO, &lab) < 0) {
462 			l_perror("ioctl DIOCWDINFO");
463 			return (1);
464 		}
465 	} else {
466 		/* Get the kernel to write the label */
467 		if (dk_ioctl(f, DIOCWDINFO, &lab) < 0) {
468 			l_perror("ioctl DIOCWDINFO");
469 			return (1);
470 		}
471 	}
472 
473 #ifdef __vax__
474 	if (lab.d_type == DTYPE_SMD && lab.d_flags & D_BADSECT &&
475 	    lab.d_secsize == 512) {
476 		/* Write the label to the odd sectors of the last track! */
477 		daddr_t	alt;
478 		int	i;
479 		uint8_t sec0[512];
480 
481 		if (pread(f, sec0, 512, 0) < 512) {
482 			warn("read master label to write alternates");
483 			return 0;
484 		}
485 
486 		alt = lab.d_ncylinders * lab.d_secpercyl - lab.d_nsectors;
487 		for (i = 1; i < 11 && i < lab.d_nsectors; i += 2) {
488 			if (pwrite(f, sec0, 512, (off_t)(alt + i) * 512) < 512)
489 				warn("alternate label %d write", i/2);
490 		}
491 	}
492 #endif	/* __vax__ */
493 
494 	return 0;
495 }
496 
497 int
498 writelabel(int f, struct disklabel *lp)
499 {
500 	if (lp != &lab)
501 		lab = *lp;
502 	return write_label(f);
503 }
504 
505 static void
506 l_perror(const char *s)
507 {
508 
509 	switch (errno) {
510 
511 	case ESRCH:
512 		warnx("%s: No disk label on disk;\n"
513 		    "use \"disklabel -I\" to install initial label", s);
514 		break;
515 
516 	case EINVAL:
517 		warnx("%s: Label magic number or checksum is wrong!\n"
518 		    "(disklabel or kernel is out of date?)", s);
519 		break;
520 
521 	case EBUSY:
522 		warnx("%s: Open partition would move or shrink", s);
523 		break;
524 
525 	case EXDEV:
526 		warnx("%s: Labeled partition or 'a' partition must start"
527 		      " at beginning of disk", s);
528 		break;
529 
530 	default:
531 		warn("%s", s);
532 		break;
533 	}
534 }
535 
536 #ifdef NO_MBR_SUPPORT
537 #define process_mbr(f, action) 1
538 #else
539 /*
540  * Scan DOS/MBR partition table and extended partition list for NetBSD ptns.
541  */
542 static int
543 process_mbr(int f, int (*action)(int, u_int))
544 {
545 	struct mbr_partition *dp;
546 	struct mbr_sector mbr;
547 	int rval = 1, res;
548 	int part;
549 	u_int ext_base, next_ext, this_ext, start;
550 
551 	ext_base = 0;
552 	next_ext = 0;
553 	for (;;) {
554 		this_ext = next_ext;
555 		next_ext = 0;
556 		if (verbose > 1)
557 			warnx("reading mbr sector %u", this_ext);
558 		if (pread(f, &mbr, sizeof mbr, this_ext * (off_t)DEV_BSIZE)
559 		    != sizeof(mbr)) {
560 			if (verbose)
561 				warn("Can't read master boot record %d",
562 				    this_ext);
563 			break;
564 		}
565 
566 		/* Check if table is valid. */
567 		if (mbr.mbr_magic != htole16(MBR_MAGIC)) {
568 			if (verbose)
569 				warnx("Invalid signature in mbr record %d",
570 				    this_ext);
571 			break;
572 		}
573 
574 		dp = &mbr.mbr_parts[0];
575 
576 		/* Find NetBSD partition(s). */
577 		for (part = 0; part < MBR_PART_COUNT; dp++, part++) {
578 			start = le32toh(dp->mbrp_start);
579 			switch (dp->mbrp_type) {
580 #ifdef COMPAT_386BSD_MBRPART
581 			case MBR_PTYPE_386BSD:
582 				if (ext_base != 0)
583 					break;
584 				/* FALLTHROUGH */
585 #endif
586 			case MBR_PTYPE_NETBSD:
587 				res = action(f, this_ext + start);
588 				if (res <= 0)
589 					/* Found or failure */
590 					return res;
591 				if (res > rval)
592 					/* Keep largest value */
593 					rval = res;
594 				break;
595 			case MBR_PTYPE_EXT:
596 			case MBR_PTYPE_EXT_LBA:
597 			case MBR_PTYPE_EXT_LNX:
598 				next_ext = start;
599 				break;
600 			default:
601 				break;
602 			}
603 		}
604 		if (next_ext == 0)
605 			/* No more extended partitions */
606 			break;
607 		next_ext += ext_base;
608 		if (ext_base == 0)
609 			ext_base = next_ext;
610 
611 		if (next_ext <= this_ext) {
612 			if (verbose)
613 				warnx("Invalid extended chain %x <= %x",
614 					next_ext, this_ext);
615 			break;
616 		}
617 		/* Maybe we should check against the disk size... */
618 	}
619 
620 	return rval;
621 }
622 
623 static int
624 readlabel_mbr(int f, u_int sector)
625 {
626 	struct disklabel *lp;
627 
628 	lp = find_label(f, sector);
629 	if (lp == NULL)
630 		return 1;
631 	lab = *lp;
632 	return 0;
633 }
634 
635 static int
636 writelabel_mbr(int f, u_int sector)
637 {
638 	return update_label(f, sector, mflag ? LABELOFFSET_MBR : ~0U) ? 2 : 0;
639 }
640 
641 #endif	/* !NO_MBR_SUPPORT */
642 
643 #ifndef USE_ACORN
644 #define get_filecore_partition(f) 0
645 #else
646 /*
647  * static int filecore_checksum(u_char *bootblock)
648  *
649  * Calculates the filecore boot block checksum. This is used to validate
650  * a filecore boot block on the disk.  If a boot block is validated then
651  * it is used to locate the partition table. If the boot block is not
652  * validated, it is assumed that the whole disk is NetBSD.
653  *
654  * The basic algorithm is:
655  *
656  *	for (each byte in block, excluding checksum) {
657  *		sum += byte;
658  *		if (sum > 255)
659  *			sum -= 255;
660  *	}
661  *
662  * That's equivalent to summing all of the bytes in the block
663  * (excluding the checksum byte, of course), then calculating the
664  * checksum as "cksum = sum - ((sum - 1) / 255) * 255)".  That
665  * expression may or may not yield a faster checksum function,
666  * but it's easier to reason about.
667  *
668  * Note that if you have a block filled with bytes of a single
669  * value "X" (regardless of that value!) and calculate the cksum
670  * of the block (excluding the checksum byte), you will _always_
671  * end up with a checksum of X.  (Do the math; that can be derived
672  * from the checksum calculation function!)  That means that
673  * blocks which contain bytes which all have the same value will
674  * always checksum properly.  That's a _very_ unlikely occurence
675  * (probably impossible, actually) for a valid filecore boot block,
676  * so we treat such blocks as invalid.
677  */
678 static int
679 filecore_checksum(u_char *bootblock)
680 {
681 	u_char	byte0, accum_diff;
682 	u_int	sum;
683 	int	i;
684 
685 	sum = 0;
686 	accum_diff = 0;
687 	byte0 = bootblock[0];
688 
689 	/*
690 	 * Sum the contents of the block, keeping track of whether
691 	 * or not all bytes are the same.  If 'accum_diff' ends up
692 	 * being zero, all of the bytes are, in fact, the same.
693 	 */
694 	for (i = 0; i < 511; ++i) {
695 		sum += bootblock[i];
696 		accum_diff |= bootblock[i] ^ byte0;
697 	}
698 
699 	/*
700 	 * Check to see if the checksum byte is the same as the
701 	 * rest of the bytes, too.  (Note that if all of the bytes
702 	 * are the same except the checksum, a checksum compare
703 	 * won't succeed, but that's not our problem.)
704 	 */
705 	accum_diff |= bootblock[i] ^ byte0;
706 
707 	/* All bytes in block are the same; call it invalid. */
708 	if (accum_diff == 0)
709 		return (-1);
710 
711 	return (sum - ((sum - 1) / 255) * 255);
712 }
713 
714 /*
715  * Check for the presence of a RiscOS filecore boot block
716  * indicating an ADFS file system on the disc.
717  * Return the offset to the NetBSD part of the disc if
718  * this can be determined.
719  * This routine will terminate disklabel if the disc
720  * is found to be ADFS only.
721  */
722 static u_int
723 get_filecore_partition(int f)
724 {
725 	struct filecore_bootblock	*fcbb;
726 	static u_char	bb[DEV_BSIZE];
727 	u_int		offset;
728 	struct riscix_partition_table	*riscix_part;
729 	int		loop;
730 
731 	if (pread(f, bb, sizeof(bb), (off_t)FILECORE_BOOT_SECTOR * DEV_BSIZE) != sizeof(bb))
732 		err(4, "can't read filecore boot block");
733 	fcbb = (struct filecore_bootblock *)bb;
734 
735 	/* Check if table is valid. */
736 	if (filecore_checksum(bb) != fcbb->checksum)
737 		return (0);
738 
739 	/*
740 	 * Check for NetBSD/arm32 (RiscBSD) partition marker.
741 	 * If found the NetBSD disklabel location is easy.
742 	 */
743 	offset = (fcbb->partition_cyl_low + (fcbb->partition_cyl_high << 8))
744 	    * fcbb->heads * fcbb->secspertrack;
745 
746 	switch (fcbb->partition_type) {
747 
748 	case PARTITION_FORMAT_RISCBSD:
749 		return (offset);
750 
751 	case PARTITION_FORMAT_RISCIX:
752 		/*
753 		 * Read the RISCiX partition table and search for the
754 		 * first partition named "RiscBSD", "NetBSD", or "Empty:"
755 		 *
756 		 * XXX is use of 'Empty:' really desirable?! -- cgd
757 		 */
758 
759 		if (pread(f, bb, sizeof(bb), (off_t)offset * DEV_BSIZE) != sizeof(bb))
760 			err(4, "can't read riscix partition table");
761 		riscix_part = (struct riscix_partition_table *)bb;
762 
763 		for (loop = 0; loop < NRISCIX_PARTITIONS; ++loop) {
764 			if (strcmp((char *)riscix_part->partitions[loop].rp_name,
765 				    "RiscBSD") == 0 ||
766 			    strcmp((char *)riscix_part->partitions[loop].rp_name,
767 				    "NetBSD") == 0 ||
768 			    strcmp((char *)riscix_part->partitions[loop].rp_name,
769 				    "Empty:") == 0) {
770 				return riscix_part->partitions[loop].rp_start;
771 				break;
772 			}
773 		}
774 		/*
775 		 * Valid filecore boot block, RISCiX partition table
776 		 * but no NetBSD partition. We should leave this
777 		 * disc alone.
778 		 */
779 		errx(4, "cannot label: no NetBSD partition found"
780 			" in RISCiX partition table");
781 
782 	default:
783 		/*
784 		 * Valid filecore boot block and no non-ADFS partition.
785 		 * This means that the whole disc is allocated for ADFS
786 		 * so do not trash ! If the user really wants to put a
787 		 * NetBSD disklabel on the disc then they should remove
788 		 * the filecore boot block first with dd.
789 		 */
790 		errx(4, "cannot label: filecore-only disk"
791 			" (no non-ADFS partition)");
792 	}
793 	return (0);
794 }
795 #endif	/* USE_ACORN */
796 
797 /*
798  * Fetch disklabel for disk to 'lab'.
799  * Use ioctl to get label unless -r flag is given.
800  */
801 static void
802 readlabel(int f)
803 {
804 	if (rflag) {
805 		/* Get label directly from disk */
806 		if (readlabel_direct(f) == 0)
807 			return;
808 		/*
809 		 * There was no label on the disk. Get the fictious one
810 		 * as a basis for initialisation.
811 		 */
812 		if (!Fflag && Iflag && (dk_ioctl(f, DIOCGDINFO, &lab) == 0 ||
813 		    dk_ioctl(f, DIOCGDEFLABEL, &lab) == 0))
814 			return;
815 	} else {
816 		/* Get label from kernel. */
817 		if (dk_ioctl(f, DIOCGDINFO, &lab) < 0)
818 			err(4, "ioctl DIOCGDINFO");
819 		return;
820 	}
821 
822 	if (read_all == 2)
823 		/* We actually found one, and printed it... */
824 		exit(0);
825 	errx(1, "could not read existing label");
826 }
827 
828 /*
829  * Reading the label from the disk is largely a case of 'hunt the label'.
830  * and since different architectures default to different places there
831  * could even be more than one label that contradict each other!
832  * For now we look in the expected place, then search through likely
833  * other locations.
834  */
835 static struct disklabel *
836 find_label(int f, u_int sector)
837 {
838 	struct disklabel *lp;
839 	int i, offset;
840 	const char *is_deleted;
841 
842 	bootarea_len = pread(f, bootarea, sizeof bootarea,
843 	    sector * (off_t)DEV_BSIZE);
844 	if (bootarea_len <= 0) {
845 		if (verbose)
846 			warn("failed to read bootarea from sector %u", sector);
847 		return NULL;
848 	}
849 
850 	if (verbose > 2)
851 		warnx("read sector %u len %u looking for label",
852 		    sector, bootarea_len);
853 
854 	/* Check expected offset first */
855 	for (offset = LABEL_OFFSET, i = -4;; offset = i += 4) {
856 		is_deleted = "";
857 		lp = (void *)((char *)bootarea + offset);
858 		if (i == LABEL_OFFSET)
859 			continue;
860 		if ((char *)(lp + 1) > (char *)bootarea + bootarea_len)
861 			break;
862 		if (lp->d_magic2 != lp->d_magic)
863 			continue;
864 		if (read_all && (lp->d_magic == DISKMAGIC_DELETED ||
865 		    lp->d_magic == DISKMAGIC_DELETED_REV)) {
866 			lp->d_magic ^= ~0u;
867 			lp->d_magic2 ^= ~0u;
868 			is_deleted = "deleted ";
869 		}
870 		if (lp->d_magic != DISKMAGIC) {
871 			/* XXX: Do something about byte-swapped labels ? */
872 			if (lp->d_magic == DISKMAGIC_REV &&
873 			    lp->d_magic2 == DISKMAGIC_REV)
874 				warnx("ignoring %sbyteswapped label"
875 				    " at offset %u from sector %u",
876 				    is_deleted, offset, sector);
877 			continue;
878 		}
879 		if (lp->d_npartitions > MAXPARTITIONS || dkcksum(lp) != 0) {
880 			if (verbose > 0)
881 				warnx("corrupt label found at offset %u in "
882 				    "sector %u", offset, sector);
883 			continue;
884 		}
885 		if (verbose > 1)
886 			warnx("%slabel found at offset %u from sector %u",
887 			    is_deleted, offset, sector);
888 		if (!read_all)
889 			return lp;
890 
891 		/* To print all the labels we have to do it here */
892 		/* XXX: maybe we should compare them? */
893 		printf("# %ssector %u offset %u bytes\n",
894 		    is_deleted, sector, offset);
895 		if (tflag)
896 			makedisktab(stdout, lp);
897 		else {
898 			showinfo(stdout, lp, specname);
899 			showpartitions(stdout, lp, Cflag);
900 		}
901 		checklabel(lp);
902 		/* Remember we've found a label */
903 		read_all = 2;
904 	}
905 	return NULL;
906 }
907 
908 static void
909 write_bootarea(int f, u_int sector)
910 {
911 	int wlen;
912 
913 	if (bootarea_len <= 0)
914 		errx(1, "attempting to write after failed read");
915 
916 #ifdef __alpha__
917 	/*
918 	 * The Alpha requires that the boot block be checksummed.
919 	 * The NetBSD/alpha disklabel.h provides a macro to do it.
920 	 */
921 	if (sector == 0) {
922 		struct alpha_boot_block *bb;
923 
924 		bb = (struct alpha_boot_block *)(void *)bootarea;
925 		bb->bb_cksum = 0;
926 		ALPHA_BOOT_BLOCK_CKSUM(bb, &bb->bb_cksum);
927 	}
928 #endif	/* __alpha__ */
929 
930 	wlen = pwrite(f, bootarea, bootarea_len, sector * (off_t)DEV_BSIZE);
931 	if (wlen == bootarea_len)
932 		return;
933 	if (wlen == -1)
934 		err(1, "disklabel write (sector %u) size %u failed",
935 		    sector, bootarea_len);
936 	errx(1, "disklabel write (sector %u) size %u truncated to %d",
937 		    sector, bootarea_len, wlen);
938 }
939 
940 static int
941 update_label(int f, u_int label_sector, u_int label_offset)
942 {
943 	struct disklabel *disk_lp;
944 
945 	disk_lp = find_label(f, label_sector);
946 
947 	if (disk_lp && Dflag) {
948 		/* Invalidate the existing label */
949 		disk_lp->d_magic ^= ~0u;
950 		disk_lp->d_magic2 ^= ~0u;
951 		if (Dflag == 2)
952 			write_bootarea(f, label_sector);
953 		/* Force label to default location */
954 		disk_lp = NULL;
955 	}
956 
957 	if (Dflag == 2)
958 		/* We are just deleting the label */
959 		return 0;
960 
961 	if (disk_lp == NULL) {
962 		if (label_offset == ~0u)
963 			return 0;
964 		/* Nothing on the disk - we need to add it */
965 		disk_lp = (void *)((char *)bootarea + label_offset);
966 		if ((char *)(disk_lp + 1) > (char *)bootarea + bootarea_len)
967 			errx(1, "no space in bootarea (sector %u) "
968 			    "to create label", label_sector);
969 	}
970 
971 	*disk_lp = lab;
972 	write_bootarea(f, label_sector);
973 	return 1;
974 }
975 
976 static void
977 writelabel_direct(int f)
978 {
979 	u_int label_sector;
980 	int written = 0;
981 	int rval;
982 
983 	label_sector = get_filecore_partition(f);
984 	if (label_sector != 0)
985 		/* The offset needs to be that from the acorn ports... */
986 		written = update_label(f, label_sector, DEV_BSIZE);
987 
988 	rval = process_mbr(f, writelabel_mbr);
989 
990 	if (rval == 2 || written)
991 		/* Don't add a label to sector 0, but update one if there */
992 		update_label(f, 0, ~0u);
993 	else
994 		update_label(f, 0, LABEL_OFFSET);
995 }
996 
997 static int
998 readlabel_direct(int f)
999 {
1000 	struct disklabel *disk_lp;
1001 	u_int filecore_partition_offset;
1002 
1003 	filecore_partition_offset = get_filecore_partition(f);
1004 	if (filecore_partition_offset != 0) {
1005 		disk_lp = find_label(f, filecore_partition_offset);
1006 		if (disk_lp != NULL) {
1007 			lab = *disk_lp;
1008 			return 0;
1009 		}
1010 	}
1011 
1012 	if (mflag && process_mbr(f, readlabel_mbr) == 0)
1013 		return 0;
1014 
1015 	disk_lp = find_label(f, 0);
1016 	if (disk_lp != NULL) {
1017 		lab = *disk_lp;
1018 		return 0;
1019 	}
1020 
1021 	if (!mflag && process_mbr(f, readlabel_mbr) == 0)
1022 		return 0;
1023 
1024 	return 1;
1025 }
1026 
1027 static void
1028 makedisktab(FILE *f, struct disklabel *lp)
1029 {
1030 	int	 i;
1031 	const char *did;
1032 	struct partition *pp;
1033 
1034 	did = "\\\n\t:";
1035 	(void) fprintf(f, "%.*s|Automatically generated label:\\\n\t:dt=",
1036 	    (int) sizeof(lp->d_typename), lp->d_typename);
1037 	if ((unsigned) lp->d_type < DKMAXTYPES)
1038 		(void) fprintf(f, "%s:", dktypenames[lp->d_type]);
1039 	else
1040 		(void) fprintf(f, "unknown%d:", lp->d_type);
1041 
1042 	(void) fprintf(f, "se#%d:", lp->d_secsize);
1043 	(void) fprintf(f, "ns#%d:", lp->d_nsectors);
1044 	(void) fprintf(f, "nt#%d:", lp->d_ntracks);
1045 	(void) fprintf(f, "sc#%d:", lp->d_secpercyl);
1046 	(void) fprintf(f, "nc#%d:", lp->d_ncylinders);
1047 
1048 	if ((lp->d_secpercyl * lp->d_ncylinders) != lp->d_secperunit) {
1049 		(void) fprintf(f, "%ssu#%d:", did, lp->d_secperunit);
1050 		did = "";
1051 	}
1052 	if (lp->d_rpm != 3600) {
1053 		(void) fprintf(f, "%srm#%d:", did, lp->d_rpm);
1054 		did = "";
1055 	}
1056 	if (lp->d_interleave != 1) {
1057 		(void) fprintf(f, "%sil#%d:", did, lp->d_interleave);
1058 		did = "";
1059 	}
1060 	if (lp->d_trackskew != 0) {
1061 		(void) fprintf(f, "%ssk#%d:", did, lp->d_trackskew);
1062 		did = "";
1063 	}
1064 	if (lp->d_cylskew != 0) {
1065 		(void) fprintf(f, "%scs#%d:", did, lp->d_cylskew);
1066 		did = "";
1067 	}
1068 	if (lp->d_headswitch != 0) {
1069 		(void) fprintf(f, "%shs#%d:", did, lp->d_headswitch);
1070 		did = "";
1071 	}
1072 	if (lp->d_trkseek != 0) {
1073 		(void) fprintf(f, "%sts#%d:", did, lp->d_trkseek);
1074 		did = "";
1075 	}
1076 #ifdef notyet
1077 	(void) fprintf(f, "drivedata: ");
1078 	for (i = NDDATA - 1; i >= 0; i--)
1079 		if (lp->d_drivedata[i])
1080 			break;
1081 	if (i < 0)
1082 		i = 0;
1083 	for (j = 0; j <= i; j++)
1084 		(void) fprintf(f, "%d ", lp->d_drivedata[j]);
1085 #endif	/* notyet */
1086 	pp = lp->d_partitions;
1087 	for (i = 0; i < lp->d_npartitions; i++, pp++) {
1088 		if (pp->p_size) {
1089 			char c = 'a' + i;
1090 			(void) fprintf(f, "\\\n\t:");
1091 			(void) fprintf(f, "p%c#%d:", c, pp->p_size);
1092 			(void) fprintf(f, "o%c#%d:", c, pp->p_offset);
1093 			if (pp->p_fstype != FS_UNUSED) {
1094 				if ((unsigned) pp->p_fstype < FSMAXTYPES)
1095 					(void) fprintf(f, "t%c=%s:", c,
1096 					    fstypenames[pp->p_fstype]);
1097 				else
1098 					(void) fprintf(f, "t%c=unknown%d:",
1099 					    c, pp->p_fstype);
1100 			}
1101 			switch (pp->p_fstype) {
1102 
1103 			case FS_UNUSED:
1104 				break;
1105 
1106 			case FS_BSDFFS:
1107 			case FS_BSDLFS:
1108 			case FS_EX2FS:
1109 			case FS_ADOS:
1110 			case FS_APPLEUFS:
1111 				(void) fprintf(f, "b%c#%d:", c,
1112 				    pp->p_fsize * pp->p_frag);
1113 				(void) fprintf(f, "f%c#%d:", c, pp->p_fsize);
1114 				break;
1115 			default:
1116 				break;
1117 			}
1118 		}
1119 	}
1120 	(void) fprintf(f, "\n");
1121 	(void) fflush(f);
1122 }
1123 
1124 static int
1125 edit(int f)
1126 {
1127 	const char *tmpdir;
1128 	char	tmpfil[MAXPATHLEN];
1129 	int	 first, ch, fd;
1130 	int	get_ok;
1131 	FILE	*fp;
1132 
1133 	if ((tmpdir = getenv("TMPDIR")) == NULL)
1134 		tmpdir = _PATH_TMP;
1135 	(void)snprintf(tmpfil, sizeof(tmpfil), "%s/%s", tmpdir, TMPFILE);
1136 	if ((fd = mkstemp(tmpfil)) == -1 || (fp = fdopen(fd, "w")) == NULL) {
1137 		warn("%s", tmpfil);
1138 		return (1);
1139 	}
1140 	(void)fchmod(fd, 0600);
1141 	showinfo(fp, &lab, specname);
1142 	showpartitions(fp, &lab, Cflag);
1143 	(void) fclose(fp);
1144 	for (;;) {
1145 		if (!editit(tmpfil))
1146 			break;
1147 		fp = fopen(tmpfil, "r");
1148 		if (fp == NULL) {
1149 			warn("%s", tmpfil);
1150 			break;
1151 		}
1152 		(void) memset(&lab, 0, sizeof(lab));
1153 		get_ok = getasciilabel(fp, &lab);
1154 		fclose(fp);
1155 		if (get_ok && write_label(f) == 0) {
1156 			(void) unlink(tmpfil);
1157 			return (0);
1158 		}
1159 		(void) printf("re-edit the label? [y]: ");
1160 		(void) fflush(stdout);
1161 		first = ch = getchar();
1162 		while (ch != '\n' && ch != EOF)
1163 			ch = getchar();
1164 		if (first == 'n' || first == 'N')
1165 			break;
1166 	}
1167 	(void)unlink(tmpfil);
1168 	return (1);
1169 }
1170 
1171 static int
1172 editit(const char *tmpfil)
1173 {
1174 	int pid, xpid;
1175 	int status;
1176 	sigset_t nsigset, osigset;
1177 
1178 	sigemptyset(&nsigset);
1179 	sigaddset(&nsigset, SIGINT);
1180 	sigaddset(&nsigset, SIGQUIT);
1181 	sigaddset(&nsigset, SIGHUP);
1182 	sigprocmask(SIG_BLOCK, &nsigset, &osigset);
1183 	while ((pid = fork()) < 0) {
1184 		if (errno != EAGAIN) {
1185 			sigprocmask(SIG_SETMASK, &osigset, (sigset_t *)0);
1186 			warn("fork");
1187 			return (0);
1188 		}
1189 		sleep(1);
1190 	}
1191 	if (pid == 0) {
1192 		const char *ed;
1193 		char *buf;
1194 		int retval;
1195 
1196 		sigprocmask(SIG_SETMASK, &osigset, (sigset_t *)0);
1197 		setgid(getgid());
1198 		setuid(getuid());
1199 		if ((ed = getenv("EDITOR")) == (char *)0)
1200 			ed = DEFEDITOR;
1201 		/*
1202 		 * Jump through a few extra hoops in case someone's editor
1203 		 * is "editor arg1 arg2".
1204 		 */
1205 		asprintf(&buf, "%s %s", ed, tmpfil);
1206 		if (!buf)
1207 			err(1, "malloc");
1208 		retval = execlp(_PATH_BSHELL, _PATH_BSHELL, "-c", buf, NULL);
1209 		if (retval == -1)
1210 			perror(ed);
1211 		exit(retval);
1212 	}
1213 	while ((xpid = wait(&status)) >= 0)
1214 		if (xpid == pid)
1215 			break;
1216 	sigprocmask(SIG_SETMASK, &osigset, (sigset_t *)0);
1217 	return (!status);
1218 }
1219 
1220 static char *
1221 skip(char *cp)
1222 {
1223 
1224 	cp += strspn(cp, " \t");
1225 	if (*cp == '\0')
1226 		return (NULL);
1227 	return (cp);
1228 }
1229 
1230 static char *
1231 word(char *cp)
1232 {
1233 
1234 	if (cp == NULL || *cp == '\0')
1235 		return (NULL);
1236 
1237 	cp += strcspn(cp, " \t");
1238 	if (*cp == '\0')
1239 		return (NULL);
1240 	*cp++ = '\0';
1241 	cp += strspn(cp, " \t");
1242 	if (*cp == '\0')
1243 		return (NULL);
1244 	return (cp);
1245 }
1246 
1247 #define _CHECKLINE \
1248 	if (tp == NULL || *tp == '\0') {			\
1249 		warnx("line %d: too few fields", lineno);	\
1250 		errors++;					\
1251 		break;						\
1252 	}
1253 
1254 #define __CHECKLINE \
1255 	if (*tp == NULL || **tp == '\0') {			\
1256 		warnx("line %d: too few fields", lineno);	\
1257 		*tp = _error_;					\
1258 		return 0;					\
1259 	}
1260 
1261 static char _error_[] = "";
1262 #define NXTNUM(n)	if ((n = nxtnum(&tp, lineno),0) + tp != _error_) \
1263 			; else goto error
1264 #define NXTXNUM(n)	if ((n = nxtxnum(&tp, lp, lineno),0) + tp != _error_) \
1265 			; else goto error
1266 
1267 static unsigned long
1268 nxtnum(char **tp, int lineno)
1269 {
1270 	char *cp;
1271 	unsigned long v;
1272 
1273 	__CHECKLINE
1274 	if (getulong(*tp, '\0', &cp, &v, UINT32_MAX) != 0) {
1275 		warnx("line %d: syntax error", lineno);
1276 		*tp = _error_;
1277 		return 0;
1278 	}
1279 	*tp = cp;
1280 	return v;
1281 }
1282 
1283 static unsigned long
1284 nxtxnum(char **tp, struct disklabel *lp, int lineno)
1285 {
1286 	char	*cp, *ncp;
1287 	unsigned long n, v;
1288 
1289 	__CHECKLINE
1290 	cp = *tp;
1291 	if (getulong(cp, '/', &ncp, &n, UINT32_MAX) != 0)
1292 		goto bad;
1293 
1294 	if (*ncp == '/') {
1295 		n *= lp->d_secpercyl;
1296 		cp = ncp + 1;
1297 		if (getulong(cp, '/', &ncp, &v, UINT32_MAX) != 0)
1298 			goto bad;
1299 		n += v * lp->d_nsectors;
1300 		cp = ncp + 1;
1301 		if (getulong(cp, '\0', &ncp, &v, UINT32_MAX) != 0)
1302 			goto bad;
1303 		n += v;
1304 	}
1305 	*tp = ncp;
1306 	return n;
1307 bad:
1308 	warnx("line %d: invalid format", lineno);
1309 	*tp = _error_;
1310 	return 0;
1311 }
1312 
1313 /*
1314  * Read an ascii label in from fd f,
1315  * in the same format as that put out by showinfo() and showpartitions(),
1316  * and fill in lp.
1317  */
1318 static int
1319 getasciilabel(FILE *f, struct disklabel *lp)
1320 {
1321 	const char *const *cpp, *s;
1322 	struct partition *pp;
1323 	char	*cp, *tp, line[BUFSIZ], tbuf[15];
1324 	int	 lineno, errors;
1325 	unsigned long v;
1326 	unsigned int part;
1327 
1328 	lineno = 0;
1329 	errors = 0;
1330 	lp->d_bbsize = BBSIZE;				/* XXX */
1331 	lp->d_sbsize = SBLOCKSIZE;			/* XXX */
1332 	while (fgets(line, sizeof(line) - 1, f)) {
1333 		lineno++;
1334 		if ((cp = strpbrk(line, "#\r\n")) != NULL)
1335 			*cp = '\0';
1336 		cp = skip(line);
1337 		if (cp == NULL)     /* blank line or comment line */
1338 			continue;
1339 		tp = strchr(cp, ':'); /* everything has a colon in it */
1340 		if (tp == NULL) {
1341 			warnx("line %d: syntax error", lineno);
1342 			errors++;
1343 			continue;
1344 		}
1345 		*tp++ = '\0', tp = skip(tp);
1346 		if (!strcmp(cp, "type")) {
1347 			if (tp == NULL) {
1348 				strlcpy(tbuf, "unknown", sizeof(tbuf));
1349 				tp = tbuf;
1350 			}
1351 			cpp = dktypenames;
1352 			for (; cpp < &dktypenames[DKMAXTYPES]; cpp++)
1353 				if ((s = *cpp) && !strcasecmp(s, tp)) {
1354 					lp->d_type = cpp - dktypenames;
1355 					goto next;
1356 				}
1357 			if (GETNUM16(tp, &v) != 0) {
1358 				warnx("line %d: syntax error", lineno);
1359 				errors++;
1360 				continue;
1361 			}
1362 			if (v >= DKMAXTYPES)
1363 				warnx("line %d: warning, unknown disk type: %s",
1364 				    lineno, tp);
1365 			lp->d_type = v;
1366 			continue;
1367 		}
1368 		if (!strcmp(cp, "flags")) {
1369 			for (v = 0; (cp = tp) && *cp != '\0';) {
1370 				tp = word(cp);
1371 				if (!strcasecmp(cp, "removable"))
1372 					v |= D_REMOVABLE;
1373 				else if (!strcasecmp(cp, "ecc"))
1374 					v |= D_ECC;
1375 				else if (!strcasecmp(cp, "badsect"))
1376 					v |= D_BADSECT;
1377 				else {
1378 					warnx("line %d: bad flag: %s",
1379 					    lineno, cp);
1380 					errors++;
1381 				}
1382 			}
1383 			lp->d_flags = v;
1384 			continue;
1385 		}
1386 		if (!strcmp(cp, "drivedata")) {
1387 			int i;
1388 
1389 			for (i = 0; (cp = tp) && *cp != '\0' && i < NDDATA;) {
1390 				if (GETNUM32(cp, &v) != 0) {
1391 					warnx("line %d: bad drive data",
1392 					    lineno);
1393 					errors++;
1394 				} else
1395 					lp->d_drivedata[i] = v;
1396 				i++;
1397 				tp = word(cp);
1398 			}
1399 			continue;
1400 		}
1401 		if (sscanf(cp, "%lu partitions", &v) == 1) {
1402 			if (v == 0 || v > MAXPARTITIONS) {
1403 				warnx("line %d: bad # of partitions", lineno);
1404 				lp->d_npartitions = MAXPARTITIONS;
1405 				errors++;
1406 			} else
1407 				lp->d_npartitions = v;
1408 			continue;
1409 		}
1410 		if (tp == NULL) {
1411 			tbuf[0] = '\0';
1412 			tp = tbuf;
1413 		}
1414 		if (!strcmp(cp, "disk")) {
1415 			strncpy(lp->d_typename, tp, sizeof(lp->d_typename));
1416 			continue;
1417 		}
1418 		if (!strcmp(cp, "label")) {
1419 			strncpy(lp->d_packname, tp, sizeof(lp->d_packname));
1420 			continue;
1421 		}
1422 		if (!strcmp(cp, "bytes/sector")) {
1423 			if (GETNUM32(tp, &v) != 0 || v <= 0 || (v % 512) != 0) {
1424 				warnx("line %d: bad %s: %s", lineno, cp, tp);
1425 				errors++;
1426 			} else
1427 				lp->d_secsize = v;
1428 			continue;
1429 		}
1430 		if (!strcmp(cp, "sectors/track")) {
1431 			if (GETNUM32(tp, &v) != 0) {
1432 				warnx("line %d: bad %s: %s", lineno, cp, tp);
1433 				errors++;
1434 			} else
1435 				lp->d_nsectors = v;
1436 			continue;
1437 		}
1438 		if (!strcmp(cp, "sectors/cylinder")) {
1439 			if (GETNUM32(tp, &v) != 0) {
1440 				warnx("line %d: bad %s: %s", lineno, cp, tp);
1441 				errors++;
1442 			} else
1443 				lp->d_secpercyl = v;
1444 			continue;
1445 		}
1446 		if (!strcmp(cp, "tracks/cylinder")) {
1447 			if (GETNUM32(tp, &v) != 0) {
1448 				warnx("line %d: bad %s: %s", lineno, cp, tp);
1449 				errors++;
1450 			} else
1451 				lp->d_ntracks = v;
1452 			continue;
1453 		}
1454 		if (!strcmp(cp, "cylinders")) {
1455 			if (GETNUM32(tp, &v) != 0) {
1456 				warnx("line %d: bad %s: %s", lineno, cp, tp);
1457 				errors++;
1458 			} else
1459 				lp->d_ncylinders = v;
1460 			continue;
1461 		}
1462 		if (!strcmp(cp, "total sectors") ||
1463 		    !strcmp(cp, "sectors/unit")) {
1464 			if (GETNUM32(tp, &v) != 0) {
1465 				warnx("line %d: bad %s: %s", lineno, cp, tp);
1466 				errors++;
1467 			} else
1468 				lp->d_secperunit = v;
1469 			continue;
1470 		}
1471 		if (!strcmp(cp, "rpm")) {
1472 			if (GETNUM16(tp, &v) != 0) {
1473 				warnx("line %d: bad %s: %s", lineno, cp, tp);
1474 				errors++;
1475 			} else
1476 				lp->d_rpm = v;
1477 			continue;
1478 		}
1479 		if (!strcmp(cp, "interleave")) {
1480 			if (GETNUM16(tp, &v) != 0) {
1481 				warnx("line %d: bad %s: %s", lineno, cp, tp);
1482 				errors++;
1483 			} else
1484 				lp->d_interleave = v;
1485 			continue;
1486 		}
1487 		if (!strcmp(cp, "trackskew")) {
1488 			if (GETNUM16(tp, &v) != 0) {
1489 				warnx("line %d: bad %s: %s", lineno, cp, tp);
1490 				errors++;
1491 			} else
1492 				lp->d_trackskew = v;
1493 			continue;
1494 		}
1495 		if (!strcmp(cp, "cylinderskew")) {
1496 			if (GETNUM16(tp, &v) != 0) {
1497 				warnx("line %d: bad %s: %s", lineno, cp, tp);
1498 				errors++;
1499 			} else
1500 				lp->d_cylskew = v;
1501 			continue;
1502 		}
1503 		if (!strcmp(cp, "headswitch")) {
1504 			if (GETNUM32(tp, &v) != 0) {
1505 				warnx("line %d: bad %s: %s", lineno, cp, tp);
1506 				errors++;
1507 			} else
1508 				lp->d_headswitch = v;
1509 			continue;
1510 		}
1511 		if (!strcmp(cp, "track-to-track seek")) {
1512 			if (GETNUM32(tp, &v) != 0) {
1513 				warnx("line %d: bad %s: %s", lineno, cp, tp);
1514 				errors++;
1515 			} else
1516 				lp->d_trkseek = v;
1517 			continue;
1518 		}
1519 		if ('a' > *cp || *cp > 'z' || cp[1] != '\0') {
1520 			warnx("line %d: unknown field: %s", lineno, cp);
1521 			errors++;
1522 			continue;
1523 		}
1524 
1525 		/* We have a partition entry */
1526 		part = *cp - 'a';
1527 
1528 		if (part >= MAXPARTITIONS) {
1529 			warnx("line %d: bad partition name: %s", lineno, cp);
1530 			errors++;
1531 			continue;
1532 		}
1533 		pp = &lp->d_partitions[part];
1534 
1535 		NXTXNUM(pp->p_size);
1536 		NXTXNUM(pp->p_offset);
1537 		/* can't use word() here because of blanks in fstypenames[] */
1538 		tp += strspn(tp, " \t");
1539 		_CHECKLINE
1540 		cp = tp;
1541 		cpp = fstypenames;
1542 		for (; cpp < &fstypenames[FSMAXTYPES]; cpp++) {
1543 			s = *cpp;
1544 			if (s == NULL ||
1545 				(cp[strlen(s)] != ' ' &&
1546 				 cp[strlen(s)] != '\t' &&
1547 				 cp[strlen(s)] != '\0'))
1548 				continue;
1549 			if (!memcmp(s, cp, strlen(s))) {
1550 				pp->p_fstype = cpp - fstypenames;
1551 				tp += strlen(s);
1552 				if (*tp == '\0')
1553 					tp = NULL;
1554 				else {
1555 					tp += strspn(tp, " \t");
1556 					if (*tp == '\0')
1557 						tp = NULL;
1558 				}
1559 				goto gottype;
1560 			}
1561 		}
1562 		tp = word(cp);
1563 		if (isdigit(*cp & 0xff)) {
1564 			if (GETNUM8(cp, &v) != 0) {
1565 				warnx("line %d: syntax error", lineno);
1566 				errors++;
1567 			}
1568 		} else
1569 			v = FSMAXTYPES;
1570 		if ((unsigned)v >= FSMAXTYPES) {
1571 			warnx("line %d: warning, unknown filesystem type: %s",
1572 			    lineno, cp);
1573 			v = FS_UNUSED;
1574 		}
1575 		pp->p_fstype = v;
1576 gottype:
1577 		switch (pp->p_fstype) {
1578 
1579 		case FS_UNUSED:				/* XXX */
1580 			NXTNUM(pp->p_fsize);
1581 			if (pp->p_fsize == 0)
1582 				break;
1583 			NXTNUM(v);
1584 			pp->p_frag = v / pp->p_fsize;
1585 			break;
1586 
1587 		case FS_BSDFFS:
1588 		case FS_ADOS:
1589 		case FS_APPLEUFS:
1590 			NXTNUM(pp->p_fsize);
1591 			if (pp->p_fsize == 0)
1592 				break;
1593 			NXTNUM(v);
1594 			pp->p_frag = v / pp->p_fsize;
1595 			NXTNUM(pp->p_cpg);
1596 			break;
1597 		case FS_BSDLFS:
1598 			NXTNUM(pp->p_fsize);
1599 			if (pp->p_fsize == 0)
1600 				break;
1601 			NXTNUM(v);
1602 			pp->p_frag = v / pp->p_fsize;
1603 			NXTNUM(pp->p_sgs);
1604 			break;
1605 		case FS_EX2FS:
1606 			NXTNUM(pp->p_fsize);
1607 			if (pp->p_fsize == 0)
1608 				break;
1609 			NXTNUM(v);
1610 			pp->p_frag = v / pp->p_fsize;
1611 			break;
1612 		case FS_ISO9660:
1613 			NXTNUM(pp->p_cdsession);
1614 			break;
1615 		default:
1616 			break;
1617 		}
1618 		continue;
1619  error:
1620 		errors++;
1621  next:
1622 		;
1623 	}
1624 	errors += checklabel(lp);
1625 	return (errors == 0);
1626 }
1627 
1628 /*
1629  * Check disklabel for errors and fill in
1630  * derived fields according to supplied values.
1631  */
1632 int
1633 checklabel(struct disklabel *lp)
1634 {
1635 	struct partition *pp, *qp;
1636 	int	i, j, errors;
1637 	char	part;
1638 
1639 	errors = 0;
1640 	if (lp->d_secsize == 0) {
1641 		warnx("sector size %d", lp->d_secsize);
1642 		return (1);
1643 	}
1644 	if (lp->d_nsectors == 0) {
1645 		warnx("sectors/track %d", lp->d_nsectors);
1646 		return (1);
1647 	}
1648 	if (lp->d_ntracks == 0) {
1649 		warnx("tracks/cylinder %d", lp->d_ntracks);
1650 		return (1);
1651 	}
1652 	if  (lp->d_ncylinders == 0) {
1653 		warnx("cylinders/unit %d", lp->d_ncylinders);
1654 		errors++;
1655 	}
1656 	if (lp->d_rpm == 0)
1657 		warnx("warning, revolutions/minute %d", lp->d_rpm);
1658 	if (lp->d_secpercyl == 0)
1659 		lp->d_secpercyl = lp->d_nsectors * lp->d_ntracks;
1660 	if (lp->d_secperunit == 0)
1661 		lp->d_secperunit = lp->d_secpercyl * lp->d_ncylinders;
1662 	if (lp->d_bbsize == 0) {
1663 		warnx("boot block size %d", lp->d_bbsize);
1664 		errors++;
1665 	} else if (lp->d_bbsize % lp->d_secsize)
1666 		warnx("warning, boot block size %% sector-size != 0");
1667 	if (lp->d_sbsize == 0) {
1668 		warnx("super block size %d", lp->d_sbsize);
1669 		errors++;
1670 	} else if (lp->d_sbsize % lp->d_secsize)
1671 		warnx("warning, super block size %% sector-size != 0");
1672 	if (lp->d_npartitions > MAXPARTITIONS)
1673 		warnx("warning, number of partitions (%d) > MAXPARTITIONS (%d)",
1674 		    lp->d_npartitions, MAXPARTITIONS);
1675 	else
1676 		for (i = MAXPARTITIONS - 1; i >= lp->d_npartitions; i--) {
1677 			part = 'a' + i;
1678 			pp = &lp->d_partitions[i];
1679 			if (pp->p_size || pp->p_offset) {
1680 				warnx("warning, partition %c increased "
1681 				    "number of partitions from %d to %d",
1682 				    part, lp->d_npartitions, i + 1);
1683 				lp->d_npartitions = i + 1;
1684 				break;
1685 			}
1686 		}
1687 	for (i = 0; i < lp->d_npartitions; i++) {
1688 		part = 'a' + i;
1689 		pp = &lp->d_partitions[i];
1690 		if (pp->p_size == 0 && pp->p_offset != 0)
1691 			warnx("warning, partition %c: size 0, but offset %d",
1692 			    part, pp->p_offset);
1693 #ifdef STRICT_CYLINDER_ALIGNMENT
1694 		if (pp->p_offset % lp->d_secpercyl) {
1695 			warnx("warning, partition %c:"
1696 			    " not starting on cylinder boundary",
1697 			    part);
1698 			errors++;
1699 		}
1700 #endif	/* STRICT_CYLINDER_ALIGNMENT */
1701 		if (pp->p_offset > lp->d_secperunit) {
1702 			warnx("partition %c: offset past end of unit", part);
1703 			errors++;
1704 		}
1705 		if (pp->p_offset + pp->p_size > lp->d_secperunit) {
1706 			warnx("partition %c: partition extends"
1707 			    " past end of unit",
1708 			    part);
1709 			errors++;
1710 		}
1711 		if (pp->p_fstype != FS_UNUSED)
1712 			for (j = i + 1; j < lp->d_npartitions; j++) {
1713 				qp = &lp->d_partitions[j];
1714 				if (qp->p_fstype == FS_UNUSED)
1715 					continue;
1716 				if (pp->p_offset < qp->p_offset + qp->p_size &&
1717 				    qp->p_offset < pp->p_offset + pp->p_size)
1718 					warnx("partitions %c and %c overlap",
1719 					    part, 'a' + j);
1720 			}
1721 	}
1722 	return (errors);
1723 }
1724 
1725 static void
1726 usage(void)
1727 {
1728 	static const struct {
1729 		const char *name;
1730 		const char *expn;
1731 	} usages[] = {
1732 	{ "[-ACFrtv] disk", "(to read label)" },
1733 	{ "-w [-DFrv] [-f disktab] disk disktype [packid]", "(to write label)" },
1734 	{ "-e [-CDFIrv] disk", "(to edit label)" },
1735 	{ "-i [-DFIrv] disk", "(to create a label interactively)" },
1736 	{ "-D [-v] disk", "(to delete existing label(s))" },
1737 	{ "-R [-DFrv] disk protofile", "(to restore label)" },
1738 	{ "[-NW] disk", "(to write disable/enable label)" },
1739 	{ NULL, NULL }
1740 	};
1741 	int i;
1742 	const char *pn = getprogname();
1743 	const char *t = "usage:";
1744 
1745 	for (i = 0; usages[i].name != NULL; i++) {
1746 		(void)fprintf(stderr, "%s %s %s\n\t%s\n",
1747 		    t, pn, usages[i].name, usages[i].expn);
1748 		t = "or";
1749 	}
1750 	exit(1);
1751 }
1752 
1753 static int
1754 getulong(const char *str, char sep, char **epp, unsigned long *ul,
1755     unsigned long max)
1756 {
1757 	char *ep;
1758 
1759 	if (epp == NULL)
1760 		epp = &ep;
1761 
1762 	*ul = strtoul(str, epp, 10);
1763 
1764 	if ((*ul ==  ULONG_MAX && errno == ERANGE) || *ul > max)
1765 		return ERANGE;
1766 
1767 	if (*str == '\0' || (**epp != '\0' && **epp != sep &&
1768 	    !isspace((unsigned char)**epp)))
1769 		return EFTYPE;
1770 
1771 	return 0;
1772 }
1773