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