1/*-
2 * SPDX-License-Identifier: BSD-4-Clause
3 *
4 * Copyright (c) 1994, 1995 Gordon W. Ross
5 * Copyright (c) 1994 Theo de Raadt
6 * All rights reserved.
7 * Copyright (c) 1987, 1993
8 *	The Regents of the University of California.  All rights reserved.
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. All advertising materials mentioning features or use of this software
22 *    must display the following acknowledgement:
23 *	This product includes software developed by the University of
24 *	California, Berkeley and its contributors.
25 *      This product includes software developed by Theo de Raadt.
26 * 4. Neither the name of the University nor the names of its contributors
27 *    may be used to endorse or promote products derived from this software
28 *    without specific prior written permission.
29 *
30 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
31 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
32 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
33 * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
34 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
35 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
36 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
37 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
38 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
39 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
40 * SUCH DAMAGE.
41 *
42 *	from: $NetBSD: disksubr.c,v 1.13 2000/12/17 22:39:18 pk $
43 */
44
45#include <sys/param.h>
46#include <stdint.h>
47#include <sys/file.h>
48#include <sys/stat.h>
49#include <sys/wait.h>
50#include <sys/disk.h>
51#define DKTYPENAMES
52#define FSTYPENAMES
53#define MAXPARTITIONS	8 /* XXX should be 20, but see PR276517 */
54#include <sys/disklabel.h>
55
56#include <unistd.h>
57#include <string.h>
58#include <stdio.h>
59#include <libgeom.h>
60#include <stdlib.h>
61#include <signal.h>
62#include <stdarg.h>
63#include <ctype.h>
64#include <err.h>
65#include <errno.h>
66
67#include "pathnames.h"
68
69static void	makelabel(const char *, struct disklabel *);
70static int	geom_class_available(const char *);
71static int	writelabel(void);
72static int	readlabel(int flag);
73static void	display(FILE *, const struct disklabel *);
74static int	edit(void);
75static int	editit(void);
76static void	fixlabel(struct disklabel *);
77static char	*skip(char *);
78static char	*word(char *);
79static int	getasciilabel(FILE *, struct disklabel *);
80static int	getasciipartspec(char *, struct disklabel *, int, int);
81static int	checklabel(struct disklabel *);
82static void	usage(void);
83static struct disklabel *getvirginlabel(void);
84
85#define	DEFEDITOR	_PATH_VI
86#define	DEFPARTITIONS	8
87
88static char	*specname;
89static char	*pname;
90static char	tmpfil[] = PATH_TMPFILE;
91
92static struct	disklabel lab;
93static u_char	bootarea[BBSIZE];
94static off_t	mediasize;
95static ssize_t	secsize;
96static char	blank[] = "";
97static char	unknown[] = "unknown";
98
99#define MAX_PART ('z')
100#define MAX_NUM_PARTS (1 + MAX_PART - 'a')
101static char    part_size_type[MAX_NUM_PARTS];
102static char    part_offset_type[MAX_NUM_PARTS];
103static int     part_set[MAX_NUM_PARTS];
104
105static int	installboot;	/* non-zero if we should install a boot program */
106static int	allfields;	/* present all fields in edit */
107static char const *xxboot;	/* primary boot */
108
109static uint32_t lba_offset;
110#ifndef LABELSECTOR
111#define LABELSECTOR -1
112#endif
113#ifndef LABELOFFSET
114#define LABELOFFSET -1
115#endif
116static int labelsoffset = LABELSECTOR;
117static int labeloffset = LABELOFFSET;
118static int bbsize = BBSIZE;
119
120static enum {
121	UNSPEC, EDIT, READ, RESTORE, WRITE, WRITEBOOT
122} op = UNSPEC;
123
124
125static int	disable_write;   /* set to disable writing to disk label */
126static int	is_file;	/* work on a file (abs. pathname), "-f" opt. */
127
128int
129main(int argc, char *argv[])
130{
131	FILE *t;
132	int ch, error, fd;
133	const char *name;
134
135	error = 0;
136	name = NULL;
137
138	fprintf(stderr,
139	    "WARNING: bsdlabel is deprecated and is not available in FreeBSD 15 or later.\n"
140	    "Please use gpart instead.\n\n");
141
142	while ((ch = getopt(argc, argv, "ABb:efm:nRrw")) != -1)
143		switch (ch) {
144			case 'A':
145				allfields = 1;
146				break;
147			case 'B':
148				++installboot;
149				break;
150			case 'b':
151				xxboot = optarg;
152				break;
153			case 'f':
154				is_file=1;
155				break;
156			case 'm':
157				if (!strcmp(optarg, "i386") ||
158				    !strcmp(optarg, "amd64")) {
159					labelsoffset = 1;
160					labeloffset = 0;
161					bbsize = 8192;
162				} else {
163					errx(1, "Unsupported architecture");
164				}
165				break;
166			case 'n':
167				disable_write = 1;
168				break;
169			case 'R':
170				if (op != UNSPEC)
171					usage();
172				op = RESTORE;
173				break;
174			case 'e':
175				if (op != UNSPEC)
176					usage();
177				op = EDIT;
178				break;
179			case 'r':
180				/*
181				 * We accept and ignore -r for compatibility with
182				 * historical disklabel usage.
183				 */
184				break;
185			case 'w':
186				if (op != UNSPEC)
187					usage();
188				op = WRITE;
189				break;
190			case '?':
191			default:
192				usage();
193		}
194	argc -= optind;
195	argv += optind;
196
197	if (argc < 1)
198		usage();
199	if (labelsoffset < 0 || labeloffset < 0)
200		errx(1, "a -m <architecture> option must be specified");
201
202	/* Figure out the names of the thing we're working on */
203	if (is_file) {
204		specname = argv[0];
205	} else {
206		specname = g_device_path(argv[0]);
207		if (specname == NULL) {
208			warn("unable to get correct path for %s", argv[0]);
209			return(1);
210		}
211		fd = open(specname, O_RDONLY);
212		if (fd < 0) {
213			warn("error opening %s", specname);
214			return(1);
215		}
216		pname = g_providername(fd);
217		if (pname == NULL) {
218			warn("error getting providername for %s", specname);
219			close(fd);
220			return(1);
221		}
222		close(fd);
223	}
224
225	if (installboot && op == UNSPEC)
226		op = WRITEBOOT;
227	else if (op == UNSPEC)
228		op = READ;
229
230	switch(op) {
231
232	case UNSPEC:
233		break;
234
235	case EDIT:
236		if (argc != 1)
237			usage();
238		readlabel(1);
239		fixlabel(&lab);
240		error = edit();
241		break;
242
243	case READ:
244		if (argc != 1)
245			usage();
246		readlabel(1);
247		display(stdout, NULL);
248		error = checklabel(NULL);
249		break;
250
251	case RESTORE:
252		if (argc != 2)
253			usage();
254		if (!(t = fopen(argv[1], "r")))
255			err(4, "fopen %s", argv[1]);
256		readlabel(0);
257		if (!getasciilabel(t, &lab))
258			exit(1);
259		error = writelabel();
260		break;
261
262	case WRITE:
263		if (argc == 2)
264			name = argv[1];
265		else if (argc == 1)
266			name = "auto";
267		else
268			usage();
269		readlabel(0);
270		makelabel(name, &lab);
271		fixlabel(&lab);
272		if (checklabel(NULL) == 0)
273			error = writelabel();
274		break;
275
276	case WRITEBOOT:
277
278		readlabel(1);
279		fixlabel(&lab);
280		if (argc == 2)
281			makelabel(argv[1], &lab);
282		if (checklabel(NULL) == 0)
283			error = writelabel();
284		break;
285	}
286	exit(error);
287}
288
289static void
290fixlabel(struct disklabel *lp)
291{
292	struct partition *dp;
293	int i;
294
295	for (i = 0; i < lp->d_npartitions; i++) {
296		if (i == RAW_PART)
297			continue;
298		if (lp->d_partitions[i].p_size)
299			return;
300	}
301
302	dp = &lp->d_partitions[0];
303	dp->p_offset = BBSIZE / secsize;
304	dp->p_size = lp->d_secperunit - dp->p_offset;
305}
306
307/*
308 * Construct a prototype disklabel from /etc/disktab.
309 */
310static void
311makelabel(const char *type, struct disklabel *lp)
312{
313	struct disklabel *dp;
314
315	if (strcmp(type, "auto") == 0)
316		dp = getvirginlabel();
317	else
318		dp = getdiskbyname(type);
319	if (dp == NULL)
320		errx(1, "%s: unknown disk type", type);
321	*lp = *dp;
322	bzero(lp->d_packname, sizeof(lp->d_packname));
323}
324
325static void
326readboot(void)
327{
328	int fd;
329	struct stat st;
330
331	if (xxboot == NULL)
332		xxboot = "/boot/boot";
333	fd = open(xxboot, O_RDONLY);
334	if (fd < 0)
335		err(1, "cannot open %s", xxboot);
336	fstat(fd, &st);
337	if (st.st_size <= BBSIZE) {
338		if (read(fd, bootarea, st.st_size) != st.st_size)
339			err(1, "read error %s", xxboot);
340		close(fd);
341		return;
342	}
343	errx(1, "boot code %s is wrong size", xxboot);
344}
345
346static int
347geom_class_available(const char *name)
348{
349	struct gclass *class;
350	struct gmesh mesh;
351	int error;
352
353	error = geom_gettree(&mesh);
354	if (error != 0)
355		errc(1, error, "Cannot get GEOM tree");
356
357	LIST_FOREACH(class, &mesh.lg_class, lg_class) {
358		if (strcmp(class->lg_name, name) == 0) {
359			geom_deletetree(&mesh);
360			return (1);
361		}
362	}
363
364	geom_deletetree(&mesh);
365
366	return (0);
367}
368
369static int
370writelabel(void)
371{
372	int i, fd, serrno;
373	struct disklabel *lp = &lab;
374
375	if (disable_write) {
376		warnx("write to disk label suppressed - label was as follows:");
377		display(stdout, NULL);
378		return (0);
379	}
380
381	lp->d_magic = DISKMAGIC;
382	lp->d_magic2 = DISKMAGIC;
383	lp->d_checksum = 0;
384	lp->d_checksum = dkcksum(lp);
385	if (installboot)
386		readboot();
387	for (i = 0; i < lab.d_npartitions; i++)
388		if (lab.d_partitions[i].p_size)
389			lab.d_partitions[i].p_offset += lba_offset;
390	bsd_disklabel_le_enc(bootarea + labeloffset + labelsoffset * lab.d_secsize,
391	    lp);
392
393	fd = open(specname, O_RDWR);
394	if (fd < 0) {
395		if (is_file) {
396			warn("cannot open file %s for writing label", specname);
397			return(1);
398		} else
399			serrno = errno;
400
401		if (geom_class_available("PART") != 0) {
402			/*
403			 * Since we weren't able open provider for
404			 * writing, then recommend user to use gpart(8).
405			 */
406			warnc(serrno,
407			    "cannot open provider %s for writing label",
408			    specname);
409			warnx("Try to use gpart(8).");
410			return (1);
411		}
412
413		warnc(serrno, "%s", specname);
414		return (1);
415	} else {
416		if (write(fd, bootarea, bbsize) != bbsize) {
417			warn("write %s", specname);
418			close (fd);
419			return (1);
420		}
421		close (fd);
422	}
423	return (0);
424}
425
426static void
427get_file_parms(int f)
428{
429	int i;
430	struct stat sb;
431
432	if (fstat(f, &sb) != 0)
433		err(4, "fstat failed");
434	i = sb.st_mode & S_IFMT;
435	if (i != S_IFREG && i != S_IFLNK)
436		errx(4, "%s is not a valid file or link", specname);
437	secsize = DEV_BSIZE;
438	mediasize = sb.st_size;
439}
440
441/*
442 * Fetch disklabel for disk.
443 */
444static int
445readlabel(int flag)
446{
447	ssize_t nbytes;
448	uint32_t lba;
449	int f, i;
450	int error;
451
452	f = open(specname, O_RDONLY);
453	if (f < 0)
454		err(1, "%s", specname);
455	if (is_file)
456		get_file_parms(f);
457	else {
458		mediasize = g_mediasize(f);
459		secsize = g_sectorsize(f);
460		if (secsize < 0 || mediasize < 0)
461			err(4, "cannot get disk geometry");
462	}
463	if (mediasize > (off_t)0xffffffff * secsize)
464		errx(1,
465		    "disks with more than 2^32-1 sectors are not supported");
466	(void)lseek(f, (off_t)0, SEEK_SET);
467	nbytes = read(f, bootarea, BBSIZE);
468	if (nbytes == -1)
469		err(4, "%s read", specname);
470	if (nbytes != BBSIZE)
471		errx(4, "couldn't read %d bytes from %s", BBSIZE, specname);
472	close (f);
473	error = bsd_disklabel_le_dec(
474	    bootarea + (labeloffset + labelsoffset * secsize),
475	    &lab, MAXPARTITIONS);
476	if (flag && error)
477		errx(1, "%s: no valid label found", specname);
478
479	if (is_file)
480		return(0);
481
482	/*
483	 * Compensate for absolute block addressing by finding the
484	 * smallest partition offset and if the offset of the 'c'
485	 * partition is equal to that, subtract it from all offsets.
486	 */
487	lba = ~0;
488	for (i = 0; i < lab.d_npartitions; i++) {
489		if (lab.d_partitions[i].p_size)
490			lba = MIN(lba, lab.d_partitions[i].p_offset);
491	}
492	if (lba != 0 && lab.d_partitions[RAW_PART].p_offset == lba) {
493		for (i = 0; i < lab.d_npartitions; i++) {
494			if (lab.d_partitions[i].p_size)
495				lab.d_partitions[i].p_offset -= lba;
496		}
497		/*
498		 * Save the offset so that we can write the label
499		 * back with absolute block addresses.
500		 */
501		lba_offset = lba;
502	}
503	return (error);
504}
505
506
507static void
508display(FILE *f, const struct disklabel *lp)
509{
510	int i, j;
511	const struct partition *pp;
512
513	if (lp == NULL)
514		lp = &lab;
515
516	fprintf(f, "# %s:\n", specname);
517	if (allfields) {
518		if (lp->d_type < DKMAXTYPES)
519			fprintf(f, "type: %s\n", dktypenames[lp->d_type]);
520		else
521			fprintf(f, "type: %u\n", lp->d_type);
522		fprintf(f, "disk: %.*s\n", (int)sizeof(lp->d_typename),
523			lp->d_typename);
524		fprintf(f, "label: %.*s\n", (int)sizeof(lp->d_packname),
525			lp->d_packname);
526		fprintf(f, "flags:");
527		if (lp->d_flags & D_REMOVABLE)
528			fprintf(f, " removeable");
529		if (lp->d_flags & D_ECC)
530			fprintf(f, " ecc");
531		if (lp->d_flags & D_BADSECT)
532			fprintf(f, " badsect");
533		fprintf(f, "\n");
534		fprintf(f, "bytes/sector: %lu\n", (u_long)lp->d_secsize);
535		fprintf(f, "sectors/track: %lu\n", (u_long)lp->d_nsectors);
536		fprintf(f, "tracks/cylinder: %lu\n", (u_long)lp->d_ntracks);
537		fprintf(f, "sectors/cylinder: %lu\n", (u_long)lp->d_secpercyl);
538		fprintf(f, "cylinders: %lu\n", (u_long)lp->d_ncylinders);
539		fprintf(f, "sectors/unit: %lu\n", (u_long)lp->d_secperunit);
540		fprintf(f, "rpm: %u\n", lp->d_rpm);
541		fprintf(f, "interleave: %u\n", lp->d_interleave);
542		fprintf(f, "trackskew: %u\n", lp->d_trackskew);
543		fprintf(f, "cylinderskew: %u\n", lp->d_cylskew);
544		fprintf(f, "headswitch: %lu\t\t# milliseconds\n",
545		    (u_long)lp->d_headswitch);
546		fprintf(f, "track-to-track seek: %ld\t# milliseconds\n",
547		    (u_long)lp->d_trkseek);
548		fprintf(f, "drivedata: ");
549		for (i = NDDATA - 1; i >= 0; i--)
550			if (lp->d_drivedata[i])
551				break;
552		if (i < 0)
553			i = 0;
554		for (j = 0; j <= i; j++)
555			fprintf(f, "%lu ", (u_long)lp->d_drivedata[j]);
556		fprintf(f, "\n\n");
557	}
558	fprintf(f, "%u partitions:\n", lp->d_npartitions);
559	fprintf(f,
560	    "#          size     offset    fstype   [fsize bsize bps/cpg]\n");
561	pp = lp->d_partitions;
562	for (i = 0; i < lp->d_npartitions; i++, pp++) {
563		if (pp->p_size) {
564			fprintf(f, "  %c: %10lu %10lu  ", 'a' + i,
565			   (u_long)pp->p_size, (u_long)pp->p_offset);
566			if (pp->p_fstype < FSMAXTYPES)
567				fprintf(f, "%8.8s", fstypenames[pp->p_fstype]);
568			else
569				fprintf(f, "%8d", pp->p_fstype);
570			switch (pp->p_fstype) {
571
572			case FS_UNUSED:				/* XXX */
573				fprintf(f, "    %5lu %5lu %2s",
574				    (u_long)pp->p_fsize,
575				    (u_long)(pp->p_fsize * pp->p_frag), "");
576				break;
577
578			case FS_BSDFFS:
579				fprintf(f, "    %5lu %5lu %5u",
580				    (u_long)pp->p_fsize,
581				    (u_long)(pp->p_fsize * pp->p_frag),
582				    pp->p_cpg);
583				break;
584
585			case FS_BSDLFS:
586				fprintf(f, "    %5lu %5lu %5d",
587				    (u_long)pp->p_fsize,
588				    (u_long)(pp->p_fsize * pp->p_frag),
589				    pp->p_cpg);
590				break;
591
592			default:
593				fprintf(f, "%20.20s", "");
594				break;
595			}
596			if (i == RAW_PART) {
597				fprintf(f, "  # \"raw\" part, don't edit");
598			}
599			fprintf(f, "\n");
600		}
601	}
602	fflush(f);
603}
604
605static int
606edit(void)
607{
608	int c, fd;
609	struct disklabel label;
610	FILE *fp;
611
612	if ((fd = mkstemp(tmpfil)) == -1 ||
613	    (fp = fdopen(fd, "w")) == NULL) {
614		warnx("can't create %s", tmpfil);
615		return (1);
616	}
617	display(fp, NULL);
618	fclose(fp);
619	for (;;) {
620		if (!editit())
621			break;
622		fp = fopen(tmpfil, "r");
623		if (fp == NULL) {
624			warnx("can't reopen %s for reading", tmpfil);
625			break;
626		}
627		bzero((char *)&label, sizeof(label));
628		c = getasciilabel(fp, &label);
629		fclose(fp);
630		if (c) {
631			lab = label;
632			if (writelabel() == 0) {
633				(void) unlink(tmpfil);
634				return (0);
635			}
636		}
637		printf("re-edit the label? [y]: ");
638		fflush(stdout);
639		c = getchar();
640		if (c != EOF && c != (int)'\n')
641			while (getchar() != (int)'\n')
642				;
643		if  (c == (int)'n')
644			break;
645	}
646	(void) unlink(tmpfil);
647	return (1);
648}
649
650static int
651editit(void)
652{
653	int pid, xpid;
654	int locstat, omask;
655	const char *ed;
656	uid_t uid;
657	gid_t gid;
658
659	omask = sigblock(sigmask(SIGINT)|sigmask(SIGQUIT)|sigmask(SIGHUP));
660	while ((pid = fork()) < 0) {
661		if (errno == EPROCLIM) {
662			warnx("you have too many processes");
663			return(0);
664		}
665		if (errno != EAGAIN) {
666			warn("fork");
667			return(0);
668		}
669		sleep(1);
670	}
671	if (pid == 0) {
672		sigsetmask(omask);
673		gid = getgid();
674		if (setresgid(gid, gid, gid) == -1)
675			err(1, "setresgid");
676		uid = getuid();
677		if (setresuid(uid, uid, uid) == -1)
678			err(1, "setresuid");
679		if ((ed = getenv("EDITOR")) == (char *)0)
680			ed = DEFEDITOR;
681		execlp(ed, ed, tmpfil, (char *)0);
682		err(1, "%s", ed);
683	}
684	while ((xpid = wait(&locstat)) >= 0)
685		if (xpid == pid)
686			break;
687	sigsetmask(omask);
688	return(!locstat);
689}
690
691static char *
692skip(char *cp)
693{
694
695	while (*cp != '\0' && isspace(*cp))
696		cp++;
697	if (*cp == '\0' || *cp == '#')
698		return (NULL);
699	return (cp);
700}
701
702static char *
703word(char *cp)
704{
705	char c;
706
707	while (*cp != '\0' && !isspace(*cp) && *cp != '#')
708		cp++;
709	if ((c = *cp) != '\0') {
710		*cp++ = '\0';
711		if (c != '#')
712			return (skip(cp));
713	}
714	return (NULL);
715}
716
717/*
718 * Read an ascii label in from fd f,
719 * in the same format as that put out by display(),
720 * and fill in lp.
721 */
722static int
723getasciilabel(FILE *f, struct disklabel *lp)
724{
725	char *cp, *endp;
726	const char **cpp;
727	u_int part;
728	char *tp, line[BUFSIZ];
729	u_long v;
730	int lineno = 0, errors = 0;
731	int i;
732
733	makelabel("auto", lp);
734	bzero(&part_set, sizeof(part_set));
735	bzero(&part_size_type, sizeof(part_size_type));
736	bzero(&part_offset_type, sizeof(part_offset_type));
737	lp->d_bbsize = BBSIZE;				/* XXX */
738	lp->d_sbsize = 0;				/* XXX */
739	while (fgets(line, sizeof(line) - 1, f)) {
740		lineno++;
741		if ((cp = strchr(line,'\n')) != NULL)
742			*cp = '\0';
743		cp = skip(line);
744		if (cp == NULL)
745			continue;
746		tp = strchr(cp, ':');
747		if (tp == NULL) {
748			fprintf(stderr, "line %d: syntax error\n", lineno);
749			errors++;
750			continue;
751		}
752		*tp++ = '\0', tp = skip(tp);
753		if (!strcmp(cp, "type")) {
754			if (tp == NULL)
755				tp = unknown;
756			cpp = dktypenames;
757			for (; cpp < &dktypenames[DKMAXTYPES]; cpp++)
758				if (*cpp && !strcmp(*cpp, tp)) {
759					lp->d_type = cpp - dktypenames;
760					break;
761				}
762			if (cpp < &dktypenames[DKMAXTYPES])
763				continue;
764			errno = 0;
765			v = strtoul(tp, &endp, 10);
766			if (errno != 0 || *endp != '\0')
767				v = DKMAXTYPES;
768			if (v >= DKMAXTYPES)
769				fprintf(stderr, "line %d:%s %lu\n", lineno,
770				    "Warning, unknown disk type", v);
771			else
772				lp->d_type = v;
773			continue;
774		}
775		if (!strcmp(cp, "flags")) {
776			for (v = 0; (cp = tp) && *cp != '\0';) {
777				tp = word(cp);
778				if (!strcmp(cp, "removeable"))
779					v |= D_REMOVABLE;
780				else if (!strcmp(cp, "ecc"))
781					v |= D_ECC;
782				else if (!strcmp(cp, "badsect"))
783					v |= D_BADSECT;
784				else {
785					fprintf(stderr,
786					    "line %d: %s: bad flag\n",
787					    lineno, cp);
788					errors++;
789				}
790			}
791			lp->d_flags = v;
792			continue;
793		}
794		if (!strcmp(cp, "drivedata")) {
795			for (i = 0; (cp = tp) && *cp != '\0' && i < NDDATA;) {
796				lp->d_drivedata[i++] = strtoul(cp, NULL, 10);
797				tp = word(cp);
798			}
799			continue;
800		}
801		if (sscanf(cp, "%lu partitions", &v) == 1) {
802			if (v > MAXPARTITIONS) {
803				fprintf(stderr,
804				    "line %d: bad # of partitions\n", lineno);
805				lp->d_npartitions = MAXPARTITIONS;
806				errors++;
807			} else if (v < DEFPARTITIONS) {
808				fprintf(stderr,
809				    "line %d: bad # of partitions\n", lineno);
810				lp->d_npartitions = DEFPARTITIONS;
811				errors++;
812			} else
813				lp->d_npartitions = v;
814			continue;
815		}
816		if (tp == NULL)
817			tp = blank;
818		if (!strcmp(cp, "disk")) {
819			strncpy(lp->d_typename, tp, sizeof (lp->d_typename));
820			continue;
821		}
822		if (!strcmp(cp, "label")) {
823			strncpy(lp->d_packname, tp, sizeof (lp->d_packname));
824			continue;
825		}
826		if (!strcmp(cp, "bytes/sector")) {
827			v = strtoul(tp, NULL, 10);
828			if (v == 0 || (v % DEV_BSIZE) != 0) {
829				fprintf(stderr,
830				    "line %d: %s: bad sector size\n",
831				    lineno, tp);
832				errors++;
833			} else
834				lp->d_secsize = v;
835			continue;
836		}
837		if (!strcmp(cp, "sectors/track")) {
838			v = strtoul(tp, NULL, 10);
839#if (ULONG_MAX != 0xffffffffUL)
840			if (v == 0 || v > 0xffffffff)
841#else
842			if (v == 0)
843#endif
844			{
845				fprintf(stderr, "line %d: %s: bad %s\n",
846				    lineno, tp, cp);
847				errors++;
848			} else
849				lp->d_nsectors = v;
850			continue;
851		}
852		if (!strcmp(cp, "sectors/cylinder")) {
853			v = strtoul(tp, NULL, 10);
854			if (v == 0) {
855				fprintf(stderr, "line %d: %s: bad %s\n",
856				    lineno, tp, cp);
857				errors++;
858			} else
859				lp->d_secpercyl = v;
860			continue;
861		}
862		if (!strcmp(cp, "tracks/cylinder")) {
863			v = strtoul(tp, NULL, 10);
864			if (v == 0) {
865				fprintf(stderr, "line %d: %s: bad %s\n",
866				    lineno, tp, cp);
867				errors++;
868			} else
869				lp->d_ntracks = v;
870			continue;
871		}
872		if (!strcmp(cp, "cylinders")) {
873			v = strtoul(tp, NULL, 10);
874			if (v == 0) {
875				fprintf(stderr, "line %d: %s: bad %s\n",
876				    lineno, tp, cp);
877				errors++;
878			} else
879				lp->d_ncylinders = v;
880			continue;
881		}
882		if (!strcmp(cp, "sectors/unit")) {
883			v = strtoul(tp, NULL, 10);
884			if (v == 0) {
885				fprintf(stderr, "line %d: %s: bad %s\n",
886				    lineno, tp, cp);
887				errors++;
888			} else
889				lp->d_secperunit = v;
890			continue;
891		}
892		if (!strcmp(cp, "rpm")) {
893			v = strtoul(tp, NULL, 10);
894			if (v == 0 || v > USHRT_MAX) {
895				fprintf(stderr, "line %d: %s: bad %s\n",
896				    lineno, tp, cp);
897				errors++;
898			} else
899				lp->d_rpm = v;
900			continue;
901		}
902		if (!strcmp(cp, "interleave")) {
903			v = strtoul(tp, NULL, 10);
904			if (v == 0 || v > USHRT_MAX) {
905				fprintf(stderr, "line %d: %s: bad %s\n",
906				    lineno, tp, cp);
907				errors++;
908			} else
909				lp->d_interleave = v;
910			continue;
911		}
912		if (!strcmp(cp, "trackskew")) {
913			v = strtoul(tp, NULL, 10);
914			if (v > USHRT_MAX) {
915				fprintf(stderr, "line %d: %s: bad %s\n",
916				    lineno, tp, cp);
917				errors++;
918			} else
919				lp->d_trackskew = v;
920			continue;
921		}
922		if (!strcmp(cp, "cylinderskew")) {
923			v = strtoul(tp, NULL, 10);
924			if (v > USHRT_MAX) {
925				fprintf(stderr, "line %d: %s: bad %s\n",
926				    lineno, tp, cp);
927				errors++;
928			} else
929				lp->d_cylskew = v;
930			continue;
931		}
932		if (!strcmp(cp, "headswitch")) {
933			v = strtoul(tp, NULL, 10);
934			lp->d_headswitch = v;
935			continue;
936		}
937		if (!strcmp(cp, "track-to-track seek")) {
938			v = strtoul(tp, NULL, 10);
939			lp->d_trkseek = v;
940			continue;
941		}
942		/* the ':' was removed above */
943		if (*cp < 'a' || *cp > MAX_PART || cp[1] != '\0') {
944			fprintf(stderr,
945			    "line %d: %s: Unknown disklabel field\n", lineno,
946			    cp);
947			errors++;
948			continue;
949		}
950
951		/* Process a partition specification line. */
952		part = *cp - 'a';
953		if (part >= lp->d_npartitions) {
954			fprintf(stderr,
955			    "line %d: partition name out of range a-%c: %s\n",
956			    lineno, 'a' + lp->d_npartitions - 1, cp);
957			errors++;
958			continue;
959		}
960		part_set[part] = 1;
961
962		if (getasciipartspec(tp, lp, part, lineno) != 0) {
963			errors++;
964			break;
965		}
966	}
967	errors += checklabel(lp);
968	return (errors == 0);
969}
970
971#define NXTNUM(n) do { \
972	if (tp == NULL) { \
973		fprintf(stderr, "line %d: too few numeric fields\n", lineno); \
974		return (1); \
975	} else { \
976		cp = tp, tp = word(cp); \
977		(n) = strtoul(cp, NULL, 10); \
978	} \
979} while (0)
980
981/* retain 1 character following number */
982#define NXTWORD(w,n) do { \
983	if (tp == NULL) { \
984		fprintf(stderr, "line %d: too few numeric fields\n", lineno); \
985		return (1); \
986	} else { \
987		char *tmp; \
988		cp = tp, tp = word(cp); \
989		(n) = strtoul(cp, &tmp, 10); \
990		if (tmp) (w) = *tmp; \
991	} \
992} while (0)
993
994/*
995 * Read a partition line into partition `part' in the specified disklabel.
996 * Return 0 on success, 1 on failure.
997 */
998static int
999getasciipartspec(char *tp, struct disklabel *lp, int part, int lineno)
1000{
1001	struct partition *pp;
1002	char *cp, *endp;
1003	const char **cpp;
1004	u_long v;
1005
1006	pp = &lp->d_partitions[part];
1007	cp = NULL;
1008
1009	v = 0;
1010	NXTWORD(part_size_type[part],v);
1011	if (v == 0 && part_size_type[part] != '*') {
1012		fprintf(stderr,
1013		    "line %d: %s: bad partition size\n", lineno, cp);
1014		return (1);
1015	}
1016	pp->p_size = v;
1017
1018	v = 0;
1019	NXTWORD(part_offset_type[part],v);
1020	if (v == 0 && part_offset_type[part] != '*' &&
1021	    part_offset_type[part] != '\0') {
1022		fprintf(stderr,
1023		    "line %d: %s: bad partition offset\n", lineno, cp);
1024		return (1);
1025	}
1026	pp->p_offset = v;
1027	if (tp == NULL) {
1028		fprintf(stderr, "line %d: missing file system type\n", lineno);
1029		return (1);
1030	}
1031	cp = tp, tp = word(cp);
1032	for (cpp = fstypenames; cpp < &fstypenames[FSMAXTYPES]; cpp++)
1033		if (*cpp && !strcmp(*cpp, cp))
1034			break;
1035	if (*cpp != NULL) {
1036		pp->p_fstype = cpp - fstypenames;
1037	} else {
1038		if (isdigit(*cp)) {
1039			errno = 0;
1040			v = strtoul(cp, &endp, 10);
1041			if (errno != 0 || *endp != '\0')
1042				v = FSMAXTYPES;
1043		} else
1044			v = FSMAXTYPES;
1045		if (v >= FSMAXTYPES) {
1046			fprintf(stderr,
1047			    "line %d: Warning, unknown file system type %s\n",
1048			    lineno, cp);
1049			v = FS_UNUSED;
1050		}
1051		pp->p_fstype = v;
1052	}
1053
1054	switch (pp->p_fstype) {
1055	case FS_UNUSED:
1056	case FS_BSDFFS:
1057	case FS_BSDLFS:
1058		/* accept defaults for fsize/frag/cpg */
1059		if (tp) {
1060			NXTNUM(pp->p_fsize);
1061			if (pp->p_fsize == 0)
1062				break;
1063			NXTNUM(v);
1064			pp->p_frag = v / pp->p_fsize;
1065			if (tp != NULL)
1066				NXTNUM(pp->p_cpg);
1067		}
1068		/* else default to 0's */
1069		break;
1070	default:
1071		break;
1072	}
1073	return (0);
1074}
1075
1076/*
1077 * Check disklabel for errors and fill in
1078 * derived fields according to supplied values.
1079 */
1080static int
1081checklabel(struct disklabel *lp)
1082{
1083	struct partition *pp;
1084	int i, errors = 0;
1085	char part;
1086	u_long base_offset, needed, total_percent, current_offset;
1087	long free_space;
1088	int seen_default_offset;
1089	int hog_part;
1090	int j;
1091	struct partition *pp2;
1092
1093	if (lp == NULL)
1094		lp = &lab;
1095
1096	if (allfields) {
1097
1098		if (lp->d_secsize == 0) {
1099			fprintf(stderr, "sector size 0\n");
1100			return (1);
1101		}
1102		if (lp->d_nsectors == 0) {
1103			fprintf(stderr, "sectors/track 0\n");
1104			return (1);
1105		}
1106		if (lp->d_ntracks == 0) {
1107			fprintf(stderr, "tracks/cylinder 0\n");
1108			return (1);
1109		}
1110		if  (lp->d_ncylinders == 0) {
1111			fprintf(stderr, "cylinders/unit 0\n");
1112			errors++;
1113		}
1114		if (lp->d_rpm == 0)
1115			warnx("revolutions/minute 0");
1116		if (lp->d_secpercyl == 0)
1117			lp->d_secpercyl = lp->d_nsectors * lp->d_ntracks;
1118		if (lp->d_secperunit == 0)
1119			lp->d_secperunit = lp->d_secpercyl * lp->d_ncylinders;
1120		if (lp->d_bbsize == 0) {
1121			fprintf(stderr, "boot block size 0\n");
1122			errors++;
1123		} else if (lp->d_bbsize % lp->d_secsize)
1124			warnx("boot block size %% sector-size != 0");
1125		if (lp->d_npartitions > MAXPARTITIONS) {
1126			warnx("number of partitions (%lu) > MAXPARTITIONS (%d)",
1127			    (u_long)lp->d_npartitions, MAXPARTITIONS);
1128			errors++;
1129		}
1130		if (lp->d_npartitions < DEFPARTITIONS) {
1131			warnx("number of partitions (%lu) < DEFPARTITIONS (%d)",
1132			    (u_long)lp->d_npartitions, DEFPARTITIONS);
1133			errors++;
1134		}
1135	} else {
1136		struct disklabel *vl;
1137
1138		vl = getvirginlabel();
1139		if (lp->d_secsize == 0)
1140			lp->d_secsize = vl->d_secsize;
1141		if (lp->d_nsectors == 0)
1142			lp->d_nsectors = vl->d_nsectors;
1143		if (lp->d_ntracks == 0)
1144			lp->d_ntracks = vl->d_ntracks;
1145		if (lp->d_ncylinders == 0)
1146			lp->d_ncylinders = vl->d_ncylinders;
1147		if (lp->d_rpm == 0)
1148			lp->d_rpm = vl->d_rpm;
1149		if (lp->d_interleave == 0)
1150			lp->d_interleave = vl->d_interleave;
1151		if (lp->d_secpercyl == 0)
1152			lp->d_secpercyl = vl->d_secpercyl;
1153		if (lp->d_secperunit == 0 ||
1154		    lp->d_secperunit > vl->d_secperunit)
1155			lp->d_secperunit = vl->d_secperunit;
1156		if (lp->d_bbsize == 0)
1157			lp->d_bbsize = vl->d_bbsize;
1158		if (lp->d_npartitions < DEFPARTITIONS ||
1159		    lp->d_npartitions > MAXPARTITIONS)
1160			lp->d_npartitions = vl->d_npartitions;
1161	}
1162
1163
1164	/* first allocate space to the partitions, then offsets */
1165	total_percent = 0; /* in percent */
1166	hog_part = -1;
1167	/* find all fixed partitions */
1168	for (i = 0; i < lp->d_npartitions; i++) {
1169		pp = &lp->d_partitions[i];
1170		if (part_set[i]) {
1171			if (part_size_type[i] == '*') {
1172				if (i == RAW_PART) {
1173					pp->p_size = lp->d_secperunit;
1174				} else {
1175					if (hog_part != -1)
1176						warnx("Too many '*' partitions (%c and %c)",
1177						    hog_part + 'a',i + 'a');
1178					else
1179						hog_part = i;
1180				}
1181			} else {
1182				off_t size;
1183
1184				size = pp->p_size;
1185				switch (part_size_type[i]) {
1186				case '%':
1187					total_percent += size;
1188					break;
1189				case 't':
1190				case 'T':
1191					size *= 1024ULL;
1192					/* FALLTHROUGH */
1193				case 'g':
1194				case 'G':
1195					size *= 1024ULL;
1196					/* FALLTHROUGH */
1197				case 'm':
1198				case 'M':
1199					size *= 1024ULL;
1200					/* FALLTHROUGH */
1201				case 'k':
1202				case 'K':
1203					size *= 1024ULL;
1204					break;
1205				case '\0':
1206					break;
1207				default:
1208					warnx("unknown multiplier suffix '%c' for partition %c (should be K, M, G or T)",
1209					    part_size_type[i], i + 'a');
1210					break;
1211				}
1212				/* don't count %'s yet */
1213				if (part_size_type[i] != '%') {
1214					/*
1215					 * for all not in sectors, convert to
1216					 * sectors
1217					 */
1218					if (part_size_type[i] != '\0') {
1219						if (size % lp->d_secsize != 0)
1220							warnx("partition %c not an integer number of sectors",
1221							    i + 'a');
1222						size /= lp->d_secsize;
1223						pp->p_size = size;
1224					}
1225				}
1226			}
1227		}
1228	}
1229
1230	/* Find out the total free space, excluding the boot block area. */
1231	base_offset = BBSIZE / secsize;
1232	free_space = 0;
1233	for (i = 0; i < lp->d_npartitions; i++) {
1234		pp = &lp->d_partitions[i];
1235		if (!part_set[i] || i == RAW_PART ||
1236		    part_size_type[i] == '%' || part_size_type[i] == '*')
1237			continue;
1238		if (pp->p_offset > base_offset)
1239			free_space += pp->p_offset - base_offset;
1240		if (pp->p_offset + pp->p_size > base_offset)
1241			base_offset = pp->p_offset + pp->p_size;
1242	}
1243	if (base_offset < lp->d_secperunit)
1244		free_space += lp->d_secperunit - base_offset;
1245
1246	/* handle % partitions - note %'s don't need to add up to 100! */
1247	if (total_percent != 0) {
1248		if (total_percent > 100) {
1249			fprintf(stderr,"total percentage %lu is greater than 100\n",
1250			    total_percent);
1251			errors++;
1252		}
1253
1254		if (free_space > 0) {
1255			for (i = 0; i < lp->d_npartitions; i++) {
1256				pp = &lp->d_partitions[i];
1257				if (part_set[i] && part_size_type[i] == '%') {
1258					/* careful of overflows! and integer roundoff */
1259					pp->p_size = ((double)pp->p_size/100) * free_space;
1260
1261					/* FIX we can lose a sector or so due to roundoff per
1262					   partition.  A more complex algorithm could avoid that */
1263				}
1264			}
1265		} else {
1266			fprintf(stderr,
1267			    "%ld sectors available to give to '*' and '%%' partitions\n",
1268			    free_space);
1269			errors++;
1270			/* fix?  set all % partitions to size 0? */
1271		}
1272	}
1273	/* give anything remaining to the hog partition */
1274	if (hog_part != -1) {
1275		/*
1276		 * Find the range of offsets usable by '*' partitions around
1277		 * the hog partition and how much space they need.
1278		 */
1279		needed = 0;
1280		base_offset = BBSIZE / secsize;
1281		for (i = hog_part - 1; i >= 0; i--) {
1282			pp = &lp->d_partitions[i];
1283			if (!part_set[i] || i == RAW_PART)
1284				continue;
1285			if (part_offset_type[i] == '*') {
1286				needed += pp->p_size;
1287				continue;
1288			}
1289			base_offset = pp->p_offset + pp->p_size;
1290			break;
1291		}
1292		current_offset = lp->d_secperunit;
1293		for (i = lp->d_npartitions - 1; i > hog_part; i--) {
1294			pp = &lp->d_partitions[i];
1295			if (!part_set[i] || i == RAW_PART)
1296				continue;
1297			if (part_offset_type[i] == '*') {
1298				needed += pp->p_size;
1299				continue;
1300			}
1301			current_offset = pp->p_offset;
1302		}
1303
1304		if (current_offset - base_offset <= needed) {
1305			fprintf(stderr, "Cannot find space for partition %c\n",
1306			    hog_part + 'a');
1307			fprintf(stderr,
1308			    "Need more than %lu sectors between %lu and %lu\n",
1309			    needed, base_offset, current_offset);
1310			errors++;
1311			lp->d_partitions[hog_part].p_size = 0;
1312		} else {
1313			lp->d_partitions[hog_part].p_size = current_offset -
1314			    base_offset - needed;
1315		}
1316	}
1317
1318	/* Now set the offsets for each partition */
1319	current_offset = BBSIZE / secsize; /* in sectors */
1320	seen_default_offset = 0;
1321	for (i = 0; i < lp->d_npartitions; i++) {
1322		part = 'a' + i;
1323		pp = &lp->d_partitions[i];
1324		if (part_set[i]) {
1325			if (part_offset_type[i] == '*') {
1326				if (i == RAW_PART) {
1327					pp->p_offset = 0;
1328				} else {
1329					pp->p_offset = current_offset;
1330					seen_default_offset = 1;
1331				}
1332			} else {
1333				/* allow them to be out of order for old-style tables */
1334				if (pp->p_offset < current_offset &&
1335				    seen_default_offset && i != RAW_PART &&
1336				    pp->p_fstype != FS_VINUM) {
1337					fprintf(stderr,
1338"Offset %ld for partition %c overlaps previous partition which ends at %lu\n",
1339					    (long)pp->p_offset,i+'a',current_offset);
1340					fprintf(stderr,
1341"Labels with any *'s for offset must be in ascending order by sector\n");
1342					errors++;
1343				} else if (pp->p_offset != current_offset &&
1344				    i != RAW_PART && seen_default_offset) {
1345					/*
1346					 * this may give unneeded warnings if
1347					 * partitions are out-of-order
1348					 */
1349					warnx(
1350"Offset %ld for partition %c doesn't match expected value %ld",
1351					    (long)pp->p_offset, i + 'a', current_offset);
1352				}
1353			}
1354			if (i != RAW_PART)
1355				current_offset = pp->p_offset + pp->p_size;
1356		}
1357	}
1358
1359	for (i = 0; i < lp->d_npartitions; i++) {
1360		part = 'a' + i;
1361		pp = &lp->d_partitions[i];
1362		if (pp->p_size == 0 && pp->p_offset != 0)
1363			warnx("partition %c: size 0, but offset %lu",
1364			    part, (u_long)pp->p_offset);
1365#ifdef notdef
1366		if (pp->p_size % lp->d_secpercyl)
1367			warnx("partition %c: size %% cylinder-size != 0",
1368			    part);
1369		if (pp->p_offset % lp->d_secpercyl)
1370			warnx("partition %c: offset %% cylinder-size != 0",
1371			    part);
1372#endif
1373		if (pp->p_offset > lp->d_secperunit) {
1374			fprintf(stderr,
1375			    "partition %c: offset past end of unit\n", part);
1376			errors++;
1377		}
1378		if (pp->p_offset + pp->p_size > lp->d_secperunit) {
1379			fprintf(stderr,
1380			"partition %c: partition extends past end of unit\n",
1381			    part);
1382			errors++;
1383		}
1384		if (i == RAW_PART) {
1385			if (pp->p_fstype != FS_UNUSED)
1386				warnx("partition %c is not marked as unused!",part);
1387			if (pp->p_offset != 0)
1388				warnx("partition %c doesn't start at 0!",part);
1389			if (pp->p_size != lp->d_secperunit)
1390				warnx("partition %c doesn't cover the whole unit!",part);
1391
1392			if ((pp->p_fstype != FS_UNUSED) || (pp->p_offset != 0) ||
1393			    (pp->p_size != lp->d_secperunit)) {
1394				warnx("An incorrect partition %c may cause problems for "
1395				    "standard system utilities",part);
1396			}
1397		}
1398
1399		/* check for overlaps */
1400		/* this will check for all possible overlaps once and only once */
1401		for (j = 0; j < i; j++) {
1402			pp2 = &lp->d_partitions[j];
1403			if (j != RAW_PART && i != RAW_PART &&
1404			    pp->p_fstype != FS_VINUM &&
1405			    pp2->p_fstype != FS_VINUM &&
1406			    part_set[i] && part_set[j]) {
1407				if (pp2->p_offset < pp->p_offset + pp->p_size &&
1408				    (pp2->p_offset + pp2->p_size > pp->p_offset ||
1409					pp2->p_offset >= pp->p_offset)) {
1410					fprintf(stderr,"partitions %c and %c overlap!\n",
1411					    j + 'a', i + 'a');
1412					errors++;
1413				}
1414			}
1415		}
1416	}
1417	for (; i < lp->d_npartitions; i++) {
1418		part = 'a' + i;
1419		pp = &lp->d_partitions[i];
1420		if (pp->p_size || pp->p_offset)
1421			warnx("unused partition %c: size %d offset %lu",
1422			    'a' + i, pp->p_size, (u_long)pp->p_offset);
1423	}
1424	return (errors);
1425}
1426
1427/*
1428 * When operating on a "virgin" disk, try getting an initial label
1429 * from the associated device driver.  This might work for all device
1430 * drivers that are able to fetch some initial device parameters
1431 * without even having access to a (BSD) disklabel, like SCSI disks,
1432 * most IDE drives, or vn devices.
1433 *
1434 * The device name must be given in its "canonical" form.
1435 */
1436static struct disklabel *
1437getvirginlabel(void)
1438{
1439	static struct disklabel loclab;
1440	struct partition *dp;
1441	int f;
1442	u_int u;
1443
1444	if ((f = open(specname, O_RDONLY)) == -1) {
1445		warn("cannot open %s", specname);
1446		return (NULL);
1447	}
1448
1449	if (is_file)
1450		get_file_parms(f);
1451	else {
1452		mediasize = g_mediasize(f);
1453		secsize = g_sectorsize(f);
1454		if (secsize < 0 || mediasize < 0) {
1455			close (f);
1456			return (NULL);
1457		}
1458	}
1459	memset(&loclab, 0, sizeof loclab);
1460	loclab.d_magic = DISKMAGIC;
1461	loclab.d_magic2 = DISKMAGIC;
1462	loclab.d_secsize = secsize;
1463	loclab.d_secperunit = mediasize / secsize;
1464
1465	/*
1466	 * Nobody in these enlightened days uses the CHS geometry for
1467	 * anything, but nonetheless try to get it right.  If we fail
1468	 * to get any good ideas from the device, construct something
1469	 * which is IBM-PC friendly.
1470	 */
1471	if (ioctl(f, DIOCGFWSECTORS, &u) == 0)
1472		loclab.d_nsectors = u;
1473	else
1474		loclab.d_nsectors = 63;
1475	if (ioctl(f, DIOCGFWHEADS, &u) == 0)
1476		loclab.d_ntracks = u;
1477	else if (loclab.d_secperunit <= 63*1*1024)
1478		loclab.d_ntracks = 1;
1479	else if (loclab.d_secperunit <= 63*16*1024)
1480		loclab.d_ntracks = 16;
1481	else
1482		loclab.d_ntracks = 255;
1483	loclab.d_secpercyl = loclab.d_ntracks * loclab.d_nsectors;
1484	loclab.d_ncylinders = loclab.d_secperunit / loclab.d_secpercyl;
1485	loclab.d_npartitions = DEFPARTITIONS;
1486
1487	/* Various (unneeded) compat stuff */
1488	loclab.d_rpm = 3600;
1489	loclab.d_bbsize = BBSIZE;
1490	loclab.d_interleave = 1;
1491	strncpy(loclab.d_typename, "amnesiac",
1492	    sizeof(loclab.d_typename));
1493
1494	dp = &loclab.d_partitions[RAW_PART];
1495	dp->p_size = loclab.d_secperunit;
1496	loclab.d_checksum = dkcksum(&loclab);
1497	close (f);
1498	return (&loclab);
1499}
1500
1501static void
1502usage(void)
1503{
1504
1505	fprintf(stderr,
1506	"%s\n%s\n%s\n%s\n%s\n%s\n%s\n%s\n%s\n%s\n%s\n%s\n%s\n%s\n",
1507	"usage: bsdlabel disk",
1508	"\t\t(to read label)",
1509	"	bsdlabel -w [-n] [-m machine] disk [type]",
1510	"\t\t(to write label with existing boot program)",
1511	"	bsdlabel -e [-n] [-m machine] disk",
1512	"\t\t(to edit label)",
1513	"	bsdlabel -R [-n] [-m machine] disk protofile",
1514	"\t\t(to restore label with existing boot program)",
1515	"	bsdlabel -B [-b boot] [-m machine] disk",
1516	"\t\t(to install boot program with existing on-disk label)",
1517	"	bsdlabel -w -B [-n] [-b boot] [-m machine] disk [type]",
1518	"\t\t(to write label and install boot program)",
1519	"	bsdlabel -R -B [-n] [-b boot] [-m machine] disk protofile",
1520		"\t\t(to restore label and install boot program)"
1521	);
1522	exit(1);
1523}
1524