bsdlabel.c revision 215704
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 215704 2010-11-22 20:10:48Z brucec $");
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		return;
374	} else if ((!alphacksum) && st.st_size <= BBSIZE) {
375		if (read(fd, bootarea, st.st_size) != st.st_size)
376			err(1, "read error %s", xxboot);
377		return;
378	}
379	errx(1, "boot code %s is wrong size", xxboot);
380}
381
382static int
383geom_bsd_available(void)
384{
385	struct gclass *class;
386	struct gmesh mesh;
387	int error;
388
389	error = geom_gettree(&mesh);
390	if (error != 0)
391		errc(1, error, "Cannot get GEOM tree");
392
393	LIST_FOREACH(class, &mesh.lg_class, lg_class) {
394		if (strcmp(class->lg_name, "BSD") == 0) {
395			geom_deletetree(&mesh);
396			return (1);
397		}
398	}
399
400	geom_deletetree(&mesh);
401
402	return (0);
403}
404
405static int
406writelabel(void)
407{
408	uint64_t *p, sum;
409	int i, fd, serrno;
410	struct gctl_req *grq;
411	char const *errstr;
412	struct disklabel *lp = &lab;
413
414	if (disable_write) {
415		warnx("write to disk label supressed - label was as follows:");
416		display(stdout, NULL);
417		return (0);
418	}
419
420	lp->d_magic = DISKMAGIC;
421	lp->d_magic2 = DISKMAGIC;
422	lp->d_checksum = 0;
423	lp->d_checksum = dkcksum(lp);
424	if (installboot)
425		readboot();
426	for (i = 0; i < lab.d_npartitions; i++)
427		if (lab.d_partitions[i].p_size)
428			lab.d_partitions[i].p_offset += lba_offset;
429	bsd_disklabel_le_enc(bootarea + labeloffset + labelsoffset * secsize,
430	    lp);
431	if (alphacksum) {
432		/* Generate the bootblock checksum for the SRM console.  */
433		for (p = (uint64_t *)bootarea, i = 0, sum = 0; i < 63; i++)
434			sum += p[i];
435		p[63] = sum;
436	}
437
438	fd = open(specname, O_RDWR);
439	if (fd < 0) {
440		if (is_file) {
441			warn("cannot open file %s for writing label", specname);
442			return(1);
443		} else
444			serrno = errno;
445
446		/* Give up if GEOM_BSD is not available. */
447		if (geom_bsd_available() == 0) {
448			warnc(serrno, "%s", specname);
449			return (1);
450		}
451
452		grq = gctl_get_handle();
453		gctl_ro_param(grq, "verb", -1, "write label");
454		gctl_ro_param(grq, "class", -1, "BSD");
455		gctl_ro_param(grq, "geom", -1, pname);
456		gctl_ro_param(grq, "label", 148+16*8,
457			bootarea + labeloffset + labelsoffset * secsize);
458		errstr = gctl_issue(grq);
459		if (errstr != NULL) {
460			warnx("%s", errstr);
461			gctl_free(grq);
462			return(1);
463		}
464		gctl_free(grq);
465		if (installboot) {
466			grq = gctl_get_handle();
467			gctl_ro_param(grq, "verb", -1, "write bootcode");
468			gctl_ro_param(grq, "class", -1, "BSD");
469			gctl_ro_param(grq, "geom", -1, pname);
470			gctl_ro_param(grq, "bootcode", BBSIZE, bootarea);
471			errstr = gctl_issue(grq);
472			if (errstr != NULL) {
473				warnx("%s", errstr);
474				gctl_free(grq);
475				return (1);
476			}
477			gctl_free(grq);
478		}
479	} else {
480		if (write(fd, bootarea, bbsize) != bbsize) {
481			warn("write %s", specname);
482			close (fd);
483			return (1);
484		}
485		close (fd);
486	}
487	return (0);
488}
489
490static void
491get_file_parms(int f)
492{
493	int i;
494	struct stat sb;
495
496	if (fstat(f, &sb) != 0)
497		err(4, "fstat failed");
498	i = sb.st_mode & S_IFMT;
499	if (i != S_IFREG && i != S_IFLNK)
500		errx(4, "%s is not a valid file or link", specname);
501	secsize = DEV_BSIZE;
502	mediasize = sb.st_size;
503}
504
505/*
506 * Fetch disklabel for disk.
507 */
508static int
509readlabel(int flag)
510{
511	ssize_t nbytes;
512	uint32_t lba;
513	int f, i;
514	int error;
515
516	f = open(specname, O_RDONLY);
517	if (f < 0)
518		err(1, "%s", specname);
519	if (is_file)
520		get_file_parms(f);
521	else {
522		mediasize = g_mediasize(f);
523		secsize = g_sectorsize(f);
524		if (secsize < 0 || mediasize < 0)
525			err(4, "cannot get disk geometry");
526	}
527	if (mediasize > (off_t)0xffffffff * secsize)
528		errx(1,
529		    "disks with more than 2^32-1 sectors are not supported");
530	(void)lseek(f, (off_t)0, SEEK_SET);
531	nbytes = read(f, bootarea, BBSIZE);
532	if (nbytes == -1)
533		err(4, "%s read", specname);
534	if (nbytes != BBSIZE)
535		errx(4, "couldn't read %d bytes from %s", BBSIZE, specname);
536	close (f);
537	error = bsd_disklabel_le_dec(
538	    bootarea + (labeloffset + labelsoffset * secsize),
539	    &lab, MAXPARTITIONS);
540	if (flag && error)
541		errx(1, "%s: no valid label found", specname);
542
543	if (is_file)
544		return(0);
545
546	/*
547	 * Compensate for absolute block addressing by finding the
548	 * smallest partition offset and if the offset of the 'c'
549	 * partition is equal to that, subtract it from all offsets.
550	 */
551	lba = ~0;
552	for (i = 0; i < lab.d_npartitions; i++) {
553		if (lab.d_partitions[i].p_size)
554			lba = MIN(lba, lab.d_partitions[i].p_offset);
555	}
556	if (lba != 0 && lab.d_partitions[RAW_PART].p_offset == lba) {
557		for (i = 0; i < lab.d_npartitions; i++) {
558			if (lab.d_partitions[i].p_size)
559				lab.d_partitions[i].p_offset -= lba;
560		}
561		/*
562		 * Save the offset so that we can write the label
563		 * back with absolute block addresses.
564		 */
565		lba_offset = lba;
566	}
567	return (error);
568}
569
570
571static void
572display(FILE *f, const struct disklabel *lp)
573{
574	int i, j;
575	const struct partition *pp;
576
577	if (lp == NULL)
578		lp = &lab;
579
580	fprintf(f, "# %s:\n", specname);
581	if (allfields) {
582		if (lp->d_type < DKMAXTYPES)
583			fprintf(f, "type: %s\n", dktypenames[lp->d_type]);
584		else
585			fprintf(f, "type: %u\n", lp->d_type);
586		fprintf(f, "disk: %.*s\n", (int)sizeof(lp->d_typename),
587			lp->d_typename);
588		fprintf(f, "label: %.*s\n", (int)sizeof(lp->d_packname),
589			lp->d_packname);
590		fprintf(f, "flags:");
591		if (lp->d_flags & D_REMOVABLE)
592			fprintf(f, " removeable");
593		if (lp->d_flags & D_ECC)
594			fprintf(f, " ecc");
595		if (lp->d_flags & D_BADSECT)
596			fprintf(f, " badsect");
597		fprintf(f, "\n");
598		fprintf(f, "bytes/sector: %lu\n", (u_long)lp->d_secsize);
599		fprintf(f, "sectors/track: %lu\n", (u_long)lp->d_nsectors);
600		fprintf(f, "tracks/cylinder: %lu\n", (u_long)lp->d_ntracks);
601		fprintf(f, "sectors/cylinder: %lu\n", (u_long)lp->d_secpercyl);
602		fprintf(f, "cylinders: %lu\n", (u_long)lp->d_ncylinders);
603		fprintf(f, "sectors/unit: %lu\n", (u_long)lp->d_secperunit);
604		fprintf(f, "rpm: %u\n", lp->d_rpm);
605		fprintf(f, "interleave: %u\n", lp->d_interleave);
606		fprintf(f, "trackskew: %u\n", lp->d_trackskew);
607		fprintf(f, "cylinderskew: %u\n", lp->d_cylskew);
608		fprintf(f, "headswitch: %lu\t\t# milliseconds\n",
609		    (u_long)lp->d_headswitch);
610		fprintf(f, "track-to-track seek: %ld\t# milliseconds\n",
611		    (u_long)lp->d_trkseek);
612		fprintf(f, "drivedata: ");
613		for (i = NDDATA - 1; i >= 0; i--)
614			if (lp->d_drivedata[i])
615				break;
616		if (i < 0)
617			i = 0;
618		for (j = 0; j <= i; j++)
619			fprintf(f, "%lu ", (u_long)lp->d_drivedata[j]);
620		fprintf(f, "\n\n");
621	}
622	fprintf(f, "%u partitions:\n", lp->d_npartitions);
623	fprintf(f,
624	    "#        size   offset    fstype   [fsize bsize bps/cpg]\n");
625	pp = lp->d_partitions;
626	for (i = 0; i < lp->d_npartitions; i++, pp++) {
627		if (pp->p_size) {
628			fprintf(f, "  %c: %8lu %8lu  ", 'a' + i,
629			   (u_long)pp->p_size, (u_long)pp->p_offset);
630			if (pp->p_fstype < FSMAXTYPES)
631				fprintf(f, "%8.8s", fstypenames[pp->p_fstype]);
632			else
633				fprintf(f, "%8d", pp->p_fstype);
634			switch (pp->p_fstype) {
635
636			case FS_UNUSED:				/* XXX */
637				fprintf(f, "    %5lu %5lu %5.5s ",
638				    (u_long)pp->p_fsize,
639				    (u_long)(pp->p_fsize * pp->p_frag), "");
640				break;
641
642			case FS_BSDFFS:
643				fprintf(f, "    %5lu %5lu %5u ",
644				    (u_long)pp->p_fsize,
645				    (u_long)(pp->p_fsize * pp->p_frag),
646				    pp->p_cpg);
647				break;
648
649			case FS_BSDLFS:
650				fprintf(f, "    %5lu %5lu %5d",
651				    (u_long)pp->p_fsize,
652				    (u_long)(pp->p_fsize * pp->p_frag),
653				    pp->p_cpg);
654				break;
655
656			default:
657				fprintf(f, "%20.20s", "");
658				break;
659			}
660			if (i == RAW_PART) {
661				fprintf(f, "  # \"raw\" part, don't edit");
662			}
663			fprintf(f, "\n");
664		}
665	}
666	fflush(f);
667}
668
669static int
670edit(void)
671{
672	int c, fd;
673	struct disklabel label;
674	FILE *fp;
675
676	if ((fd = mkstemp(tmpfil)) == -1 ||
677	    (fp = fdopen(fd, "w")) == NULL) {
678		warnx("can't create %s", tmpfil);
679		return (1);
680	}
681	display(fp, NULL);
682	fclose(fp);
683	for (;;) {
684		if (!editit())
685			break;
686		fp = fopen(tmpfil, "r");
687		if (fp == NULL) {
688			warnx("can't reopen %s for reading", tmpfil);
689			break;
690		}
691		bzero((char *)&label, sizeof(label));
692		c = getasciilabel(fp, &label);
693		fclose(fp);
694		if (c) {
695			lab = label;
696			if (writelabel() == 0) {
697				(void) unlink(tmpfil);
698				return (0);
699			}
700		}
701		printf("re-edit the label? [y]: ");
702		fflush(stdout);
703		c = getchar();
704		if (c != EOF && c != (int)'\n')
705			while (getchar() != (int)'\n')
706				;
707		if  (c == (int)'n')
708			break;
709	}
710	(void) unlink(tmpfil);
711	return (1);
712}
713
714static int
715editit(void)
716{
717	int pid, xpid;
718	int locstat, omask;
719	const char *ed;
720	uid_t uid;
721	gid_t gid;
722
723	omask = sigblock(sigmask(SIGINT)|sigmask(SIGQUIT)|sigmask(SIGHUP));
724	while ((pid = fork()) < 0) {
725		if (errno == EPROCLIM) {
726			warnx("you have too many processes");
727			return(0);
728		}
729		if (errno != EAGAIN) {
730			warn("fork");
731			return(0);
732		}
733		sleep(1);
734	}
735	if (pid == 0) {
736		sigsetmask(omask);
737		gid = getgid();
738		if (setresgid(gid, gid, gid) == -1)
739			err(1, "setresgid");
740		uid = getuid();
741		if (setresuid(uid, uid, uid) == -1)
742			err(1, "setresuid");
743		if ((ed = getenv("EDITOR")) == (char *)0)
744			ed = DEFEDITOR;
745		execlp(ed, ed, tmpfil, (char *)0);
746		err(1, "%s", ed);
747	}
748	while ((xpid = wait(&locstat)) >= 0)
749		if (xpid == pid)
750			break;
751	sigsetmask(omask);
752	return(!locstat);
753}
754
755static char *
756skip(char *cp)
757{
758
759	while (*cp != '\0' && isspace(*cp))
760		cp++;
761	if (*cp == '\0' || *cp == '#')
762		return (NULL);
763	return (cp);
764}
765
766static char *
767word(char *cp)
768{
769	char c;
770
771	while (*cp != '\0' && !isspace(*cp) && *cp != '#')
772		cp++;
773	if ((c = *cp) != '\0') {
774		*cp++ = '\0';
775		if (c != '#')
776			return (skip(cp));
777	}
778	return (NULL);
779}
780
781/*
782 * Read an ascii label in from fd f,
783 * in the same format as that put out by display(),
784 * and fill in lp.
785 */
786static int
787getasciilabel(FILE *f, struct disklabel *lp)
788{
789	char *cp, *endp;
790	const char **cpp;
791	u_int part;
792	char *tp, line[BUFSIZ];
793	u_long v;
794	int lineno = 0, errors = 0;
795	int i;
796
797	makelabel("auto", lp);
798	bzero(&part_set, sizeof(part_set));
799	bzero(&part_size_type, sizeof(part_size_type));
800	bzero(&part_offset_type, sizeof(part_offset_type));
801	lp->d_bbsize = BBSIZE;				/* XXX */
802	lp->d_sbsize = 0;				/* XXX */
803	while (fgets(line, sizeof(line) - 1, f)) {
804		lineno++;
805		if ((cp = index(line,'\n')) != 0)
806			*cp = '\0';
807		cp = skip(line);
808		if (cp == NULL)
809			continue;
810		tp = index(cp, ':');
811		if (tp == NULL) {
812			fprintf(stderr, "line %d: syntax error\n", lineno);
813			errors++;
814			continue;
815		}
816		*tp++ = '\0', tp = skip(tp);
817		if (!strcmp(cp, "type")) {
818			if (tp == NULL)
819				tp = unknown;
820			cpp = dktypenames;
821			for (; cpp < &dktypenames[DKMAXTYPES]; cpp++)
822				if (*cpp && !strcmp(*cpp, tp)) {
823					lp->d_type = cpp - dktypenames;
824					break;
825				}
826			if (cpp < &dktypenames[DKMAXTYPES])
827				continue;
828			errno = 0;
829			v = strtoul(tp, &endp, 10);
830			if (errno != 0 || *endp != '\0')
831				v = DKMAXTYPES;
832			if (v >= DKMAXTYPES)
833				fprintf(stderr, "line %d:%s %lu\n", lineno,
834				    "Warning, unknown disk type", v);
835			else
836				lp->d_type = v;
837			continue;
838		}
839		if (!strcmp(cp, "flags")) {
840			for (v = 0; (cp = tp) && *cp != '\0';) {
841				tp = word(cp);
842				if (!strcmp(cp, "removeable"))
843					v |= D_REMOVABLE;
844				else if (!strcmp(cp, "ecc"))
845					v |= D_ECC;
846				else if (!strcmp(cp, "badsect"))
847					v |= D_BADSECT;
848				else {
849					fprintf(stderr,
850					    "line %d: %s: bad flag\n",
851					    lineno, cp);
852					errors++;
853				}
854			}
855			lp->d_flags = v;
856			continue;
857		}
858		if (!strcmp(cp, "drivedata")) {
859			for (i = 0; (cp = tp) && *cp != '\0' && i < NDDATA;) {
860				lp->d_drivedata[i++] = strtoul(cp, NULL, 10);
861				tp = word(cp);
862			}
863			continue;
864		}
865		if (sscanf(cp, "%lu partitions", &v) == 1) {
866			if (v == 0 || v > MAXPARTITIONS) {
867				fprintf(stderr,
868				    "line %d: bad # of partitions\n", lineno);
869				lp->d_npartitions = MAXPARTITIONS;
870				errors++;
871			} else
872				lp->d_npartitions = v;
873			continue;
874		}
875		if (tp == NULL)
876			tp = blank;
877		if (!strcmp(cp, "disk")) {
878			strncpy(lp->d_typename, tp, sizeof (lp->d_typename));
879			continue;
880		}
881		if (!strcmp(cp, "label")) {
882			strncpy(lp->d_packname, tp, sizeof (lp->d_packname));
883			continue;
884		}
885		if (!strcmp(cp, "bytes/sector")) {
886			v = strtoul(tp, NULL, 10);
887			if (v == 0 || (v % DEV_BSIZE) != 0) {
888				fprintf(stderr,
889				    "line %d: %s: bad sector size\n",
890				    lineno, tp);
891				errors++;
892			} else
893				lp->d_secsize = v;
894			continue;
895		}
896		if (!strcmp(cp, "sectors/track")) {
897			v = strtoul(tp, NULL, 10);
898#if (ULONG_MAX != 0xffffffffUL)
899			if (v == 0 || v > 0xffffffff)
900#else
901			if (v == 0)
902#endif
903			{
904				fprintf(stderr, "line %d: %s: bad %s\n",
905				    lineno, tp, cp);
906				errors++;
907			} else
908				lp->d_nsectors = v;
909			continue;
910		}
911		if (!strcmp(cp, "sectors/cylinder")) {
912			v = strtoul(tp, NULL, 10);
913			if (v == 0) {
914				fprintf(stderr, "line %d: %s: bad %s\n",
915				    lineno, tp, cp);
916				errors++;
917			} else
918				lp->d_secpercyl = v;
919			continue;
920		}
921		if (!strcmp(cp, "tracks/cylinder")) {
922			v = strtoul(tp, NULL, 10);
923			if (v == 0) {
924				fprintf(stderr, "line %d: %s: bad %s\n",
925				    lineno, tp, cp);
926				errors++;
927			} else
928				lp->d_ntracks = v;
929			continue;
930		}
931		if (!strcmp(cp, "cylinders")) {
932			v = strtoul(tp, NULL, 10);
933			if (v == 0) {
934				fprintf(stderr, "line %d: %s: bad %s\n",
935				    lineno, tp, cp);
936				errors++;
937			} else
938				lp->d_ncylinders = v;
939			continue;
940		}
941		if (!strcmp(cp, "sectors/unit")) {
942			v = strtoul(tp, NULL, 10);
943			if (v == 0) {
944				fprintf(stderr, "line %d: %s: bad %s\n",
945				    lineno, tp, cp);
946				errors++;
947			} else
948				lp->d_secperunit = v;
949			continue;
950		}
951		if (!strcmp(cp, "rpm")) {
952			v = strtoul(tp, NULL, 10);
953			if (v == 0 || v > USHRT_MAX) {
954				fprintf(stderr, "line %d: %s: bad %s\n",
955				    lineno, tp, cp);
956				errors++;
957			} else
958				lp->d_rpm = v;
959			continue;
960		}
961		if (!strcmp(cp, "interleave")) {
962			v = strtoul(tp, NULL, 10);
963			if (v == 0 || v > USHRT_MAX) {
964				fprintf(stderr, "line %d: %s: bad %s\n",
965				    lineno, tp, cp);
966				errors++;
967			} else
968				lp->d_interleave = v;
969			continue;
970		}
971		if (!strcmp(cp, "trackskew")) {
972			v = strtoul(tp, NULL, 10);
973			if (v > USHRT_MAX) {
974				fprintf(stderr, "line %d: %s: bad %s\n",
975				    lineno, tp, cp);
976				errors++;
977			} else
978				lp->d_trackskew = v;
979			continue;
980		}
981		if (!strcmp(cp, "cylinderskew")) {
982			v = strtoul(tp, NULL, 10);
983			if (v > USHRT_MAX) {
984				fprintf(stderr, "line %d: %s: bad %s\n",
985				    lineno, tp, cp);
986				errors++;
987			} else
988				lp->d_cylskew = v;
989			continue;
990		}
991		if (!strcmp(cp, "headswitch")) {
992			v = strtoul(tp, NULL, 10);
993			lp->d_headswitch = v;
994			continue;
995		}
996		if (!strcmp(cp, "track-to-track seek")) {
997			v = strtoul(tp, NULL, 10);
998			lp->d_trkseek = v;
999			continue;
1000		}
1001		/* the ':' was removed above */
1002		if (*cp < 'a' || *cp > MAX_PART || cp[1] != '\0') {
1003			fprintf(stderr,
1004			    "line %d: %s: Unknown disklabel field\n", lineno,
1005			    cp);
1006			errors++;
1007			continue;
1008		}
1009
1010		/* Process a partition specification line. */
1011		part = *cp - 'a';
1012		if (part >= lp->d_npartitions) {
1013			fprintf(stderr,
1014			    "line %d: partition name out of range a-%c: %s\n",
1015			    lineno, 'a' + lp->d_npartitions - 1, cp);
1016			errors++;
1017			continue;
1018		}
1019		part_set[part] = 1;
1020
1021		if (getasciipartspec(tp, lp, part, lineno) != 0) {
1022			errors++;
1023			break;
1024		}
1025	}
1026	errors += checklabel(lp);
1027	return (errors == 0);
1028}
1029
1030#define NXTNUM(n) do { \
1031	if (tp == NULL) { \
1032		fprintf(stderr, "line %d: too few numeric fields\n", lineno); \
1033		return (1); \
1034	} else { \
1035		cp = tp, tp = word(cp); \
1036		(n) = strtoul(cp, NULL, 10); \
1037	} \
1038} while (0)
1039
1040/* retain 1 character following number */
1041#define NXTWORD(w,n) do { \
1042	if (tp == NULL) { \
1043		fprintf(stderr, "line %d: too few numeric fields\n", lineno); \
1044		return (1); \
1045	} else { \
1046		char *tmp; \
1047		cp = tp, tp = word(cp); \
1048		(n) = strtoul(cp, &tmp, 10); \
1049		if (tmp) (w) = *tmp; \
1050	} \
1051} while (0)
1052
1053/*
1054 * Read a partition line into partition `part' in the specified disklabel.
1055 * Return 0 on success, 1 on failure.
1056 */
1057static int
1058getasciipartspec(char *tp, struct disklabel *lp, int part, int lineno)
1059{
1060	struct partition *pp;
1061	char *cp, *endp;
1062	const char **cpp;
1063	u_long v;
1064
1065	pp = &lp->d_partitions[part];
1066	cp = NULL;
1067
1068	v = 0;
1069	NXTWORD(part_size_type[part],v);
1070	if (v == 0 && part_size_type[part] != '*') {
1071		fprintf(stderr,
1072		    "line %d: %s: bad partition size\n", lineno, cp);
1073		return (1);
1074	}
1075	pp->p_size = v;
1076
1077	v = 0;
1078	NXTWORD(part_offset_type[part],v);
1079	if (v == 0 && part_offset_type[part] != '*' &&
1080	    part_offset_type[part] != '\0') {
1081		fprintf(stderr,
1082		    "line %d: %s: bad partition offset\n", lineno, cp);
1083		return (1);
1084	}
1085	pp->p_offset = v;
1086	if (tp == NULL) {
1087		fprintf(stderr, "line %d: missing file system type\n", lineno);
1088		return (1);
1089	}
1090	cp = tp, tp = word(cp);
1091	for (cpp = fstypenames; cpp < &fstypenames[FSMAXTYPES]; cpp++)
1092		if (*cpp && !strcmp(*cpp, cp))
1093			break;
1094	if (*cpp != NULL) {
1095		pp->p_fstype = cpp - fstypenames;
1096	} else {
1097		if (isdigit(*cp)) {
1098			errno = 0;
1099			v = strtoul(cp, &endp, 10);
1100			if (errno != 0 || *endp != '\0')
1101				v = FSMAXTYPES;
1102		} else
1103			v = FSMAXTYPES;
1104		if (v >= FSMAXTYPES) {
1105			fprintf(stderr,
1106			    "line %d: Warning, unknown file system type %s\n",
1107			    lineno, cp);
1108			v = FS_UNUSED;
1109		}
1110		pp->p_fstype = v;
1111	}
1112
1113	switch (pp->p_fstype) {
1114	case FS_UNUSED:
1115	case FS_BSDFFS:
1116	case FS_BSDLFS:
1117		/* accept defaults for fsize/frag/cpg */
1118		if (tp) {
1119			NXTNUM(pp->p_fsize);
1120			if (pp->p_fsize == 0)
1121				break;
1122			NXTNUM(v);
1123			pp->p_frag = v / pp->p_fsize;
1124			if (tp != NULL)
1125				NXTNUM(pp->p_cpg);
1126		}
1127		/* else default to 0's */
1128		break;
1129	default:
1130		break;
1131	}
1132	return (0);
1133}
1134
1135/*
1136 * Check disklabel for errors and fill in
1137 * derived fields according to supplied values.
1138 */
1139static int
1140checklabel(struct disklabel *lp)
1141{
1142	struct partition *pp;
1143	int i, errors = 0;
1144	char part;
1145	u_long base_offset, needed, total_size, total_percent, current_offset;
1146	long free_space;
1147	int seen_default_offset;
1148	int hog_part;
1149	int j;
1150	struct partition *pp2;
1151
1152	if (lp == NULL)
1153		lp = &lab;
1154
1155	if (allfields) {
1156
1157		if (lp->d_secsize == 0) {
1158			fprintf(stderr, "sector size 0\n");
1159			return (1);
1160		}
1161		if (lp->d_nsectors == 0) {
1162			fprintf(stderr, "sectors/track 0\n");
1163			return (1);
1164		}
1165		if (lp->d_ntracks == 0) {
1166			fprintf(stderr, "tracks/cylinder 0\n");
1167			return (1);
1168		}
1169		if  (lp->d_ncylinders == 0) {
1170			fprintf(stderr, "cylinders/unit 0\n");
1171			errors++;
1172		}
1173		if (lp->d_rpm == 0)
1174			warnx("revolutions/minute 0");
1175		if (lp->d_secpercyl == 0)
1176			lp->d_secpercyl = lp->d_nsectors * lp->d_ntracks;
1177		if (lp->d_secperunit == 0)
1178			lp->d_secperunit = lp->d_secpercyl * lp->d_ncylinders;
1179		if (lp->d_bbsize == 0) {
1180			fprintf(stderr, "boot block size 0\n");
1181			errors++;
1182		} else if (lp->d_bbsize % lp->d_secsize)
1183			warnx("boot block size %% sector-size != 0");
1184		if (lp->d_npartitions > MAXPARTITIONS)
1185			warnx("number of partitions (%lu) > MAXPARTITIONS (%d)",
1186			    (u_long)lp->d_npartitions, MAXPARTITIONS);
1187	} else {
1188		struct disklabel *vl;
1189
1190		vl = getvirginlabel();
1191		lp->d_secsize = vl->d_secsize;
1192		lp->d_nsectors = vl->d_nsectors;
1193		lp->d_ntracks = vl->d_ntracks;
1194		lp->d_ncylinders = vl->d_ncylinders;
1195		lp->d_rpm = vl->d_rpm;
1196		lp->d_interleave = vl->d_interleave;
1197		lp->d_secpercyl = vl->d_secpercyl;
1198		lp->d_secperunit = vl->d_secperunit;
1199		lp->d_bbsize = vl->d_bbsize;
1200		lp->d_npartitions = vl->d_npartitions;
1201	}
1202
1203
1204	/* first allocate space to the partitions, then offsets */
1205	total_size = 0; /* in sectors */
1206	total_percent = 0; /* in percent */
1207	hog_part = -1;
1208	/* find all fixed partitions */
1209	for (i = 0; i < lp->d_npartitions; i++) {
1210		pp = &lp->d_partitions[i];
1211		if (part_set[i]) {
1212			if (part_size_type[i] == '*') {
1213				if (i == RAW_PART) {
1214					pp->p_size = lp->d_secperunit;
1215				} else {
1216					if (hog_part != -1)
1217						warnx("Too many '*' partitions (%c and %c)",
1218						    hog_part + 'a',i + 'a');
1219					else
1220						hog_part = i;
1221				}
1222			} else {
1223				off_t size;
1224
1225				size = pp->p_size;
1226				switch (part_size_type[i]) {
1227				case '%':
1228					total_percent += size;
1229					break;
1230				case 't':
1231				case 'T':
1232					size *= 1024ULL;
1233					/* FALLTHROUGH */
1234				case 'g':
1235				case 'G':
1236					size *= 1024ULL;
1237					/* FALLTHROUGH */
1238				case 'm':
1239				case 'M':
1240					size *= 1024ULL;
1241					/* FALLTHROUGH */
1242				case 'k':
1243				case 'K':
1244					size *= 1024ULL;
1245					break;
1246				case '\0':
1247					break;
1248				default:
1249					warnx("unknown multiplier suffix '%c' for partition %c (should be K, M, G or T)",
1250					    part_size_type[i], i + 'a');
1251					break;
1252				}
1253				/* don't count %'s yet */
1254				if (part_size_type[i] != '%') {
1255					/*
1256					 * for all not in sectors, convert to
1257					 * sectors
1258					 */
1259					if (part_size_type[i] != '\0') {
1260						if (size % lp->d_secsize != 0)
1261							warnx("partition %c not an integer number of sectors",
1262							    i + 'a');
1263						size /= lp->d_secsize;
1264						pp->p_size = size;
1265					}
1266					/* else already in sectors */
1267					if (i != RAW_PART)
1268						total_size += size;
1269				}
1270			}
1271		}
1272	}
1273
1274	/* Find out the total free space, excluding the boot block area. */
1275	base_offset = BBSIZE / secsize;
1276	free_space = 0;
1277	for (i = 0; i < lp->d_npartitions; i++) {
1278		pp = &lp->d_partitions[i];
1279		if (!part_set[i] || i == RAW_PART ||
1280		    part_size_type[i] == '%' || part_size_type[i] == '*')
1281			continue;
1282		if (pp->p_offset > base_offset)
1283			free_space += pp->p_offset - base_offset;
1284		if (pp->p_offset + pp->p_size > base_offset)
1285			base_offset = pp->p_offset + pp->p_size;
1286	}
1287	if (base_offset < lp->d_secperunit)
1288		free_space += lp->d_secperunit - base_offset;
1289
1290	/* handle % partitions - note %'s don't need to add up to 100! */
1291	if (total_percent != 0) {
1292		if (total_percent > 100) {
1293			fprintf(stderr,"total percentage %lu is greater than 100\n",
1294			    total_percent);
1295			errors++;
1296		}
1297
1298		if (free_space > 0) {
1299			for (i = 0; i < lp->d_npartitions; i++) {
1300				pp = &lp->d_partitions[i];
1301				if (part_set[i] && part_size_type[i] == '%') {
1302					/* careful of overflows! and integer roundoff */
1303					pp->p_size = ((double)pp->p_size/100) * free_space;
1304					total_size += pp->p_size;
1305
1306					/* FIX we can lose a sector or so due to roundoff per
1307					   partition.  A more complex algorithm could avoid that */
1308				}
1309			}
1310		} else {
1311			fprintf(stderr,
1312			    "%ld sectors available to give to '*' and '%%' partitions\n",
1313			    free_space);
1314			errors++;
1315			/* fix?  set all % partitions to size 0? */
1316		}
1317	}
1318	/* give anything remaining to the hog partition */
1319	if (hog_part != -1) {
1320		/*
1321		 * Find the range of offsets usable by '*' partitions around
1322		 * the hog partition and how much space they need.
1323		 */
1324		needed = 0;
1325		base_offset = BBSIZE / secsize;
1326		for (i = hog_part - 1; i >= 0; i--) {
1327			pp = &lp->d_partitions[i];
1328			if (!part_set[i] || i == RAW_PART)
1329				continue;
1330			if (part_offset_type[i] == '*') {
1331				needed += pp->p_size;
1332				continue;
1333			}
1334			base_offset = pp->p_offset + pp->p_size;
1335			break;
1336		}
1337		current_offset = lp->d_secperunit;
1338		for (i = lp->d_npartitions - 1; i > hog_part; i--) {
1339			pp = &lp->d_partitions[i];
1340			if (!part_set[i] || i == RAW_PART)
1341				continue;
1342			if (part_offset_type[i] == '*') {
1343				needed += pp->p_size;
1344				continue;
1345			}
1346			current_offset = pp->p_offset;
1347		}
1348
1349		if (current_offset - base_offset <= needed) {
1350			fprintf(stderr, "Cannot find space for partition %c\n",
1351			    hog_part + 'a');
1352			fprintf(stderr,
1353			    "Need more than %lu sectors between %lu and %lu\n",
1354			    needed, base_offset, current_offset);
1355			errors++;
1356			lp->d_partitions[hog_part].p_size = 0;
1357		} else {
1358			lp->d_partitions[hog_part].p_size = current_offset -
1359			    base_offset - needed;
1360			total_size += lp->d_partitions[hog_part].p_size;
1361		}
1362	}
1363
1364	/* Now set the offsets for each partition */
1365	current_offset = BBSIZE / secsize; /* in sectors */
1366	seen_default_offset = 0;
1367	for (i = 0; i < lp->d_npartitions; i++) {
1368		part = 'a' + i;
1369		pp = &lp->d_partitions[i];
1370		if (part_set[i]) {
1371			if (part_offset_type[i] == '*') {
1372				if (i == RAW_PART) {
1373					pp->p_offset = 0;
1374				} else {
1375					pp->p_offset = current_offset;
1376					seen_default_offset = 1;
1377				}
1378			} else {
1379				/* allow them to be out of order for old-style tables */
1380				if (pp->p_offset < current_offset &&
1381				    seen_default_offset && i != RAW_PART &&
1382				    pp->p_fstype != FS_VINUM) {
1383					fprintf(stderr,
1384"Offset %ld for partition %c overlaps previous partition which ends at %lu\n",
1385					    (long)pp->p_offset,i+'a',current_offset);
1386					fprintf(stderr,
1387"Labels with any *'s for offset must be in ascending order by sector\n");
1388					errors++;
1389				} else if (pp->p_offset != current_offset &&
1390				    i != RAW_PART && seen_default_offset) {
1391					/*
1392					 * this may give unneeded warnings if
1393					 * partitions are out-of-order
1394					 */
1395					warnx(
1396"Offset %ld for partition %c doesn't match expected value %ld",
1397					    (long)pp->p_offset, i + 'a', current_offset);
1398				}
1399			}
1400			if (i != RAW_PART)
1401				current_offset = pp->p_offset + pp->p_size;
1402		}
1403	}
1404
1405	for (i = 0; i < lp->d_npartitions; i++) {
1406		part = 'a' + i;
1407		pp = &lp->d_partitions[i];
1408		if (pp->p_size == 0 && pp->p_offset != 0)
1409			warnx("partition %c: size 0, but offset %lu",
1410			    part, (u_long)pp->p_offset);
1411#ifdef notdef
1412		if (pp->p_size % lp->d_secpercyl)
1413			warnx("partition %c: size %% cylinder-size != 0",
1414			    part);
1415		if (pp->p_offset % lp->d_secpercyl)
1416			warnx("partition %c: offset %% cylinder-size != 0",
1417			    part);
1418#endif
1419		if (pp->p_offset > lp->d_secperunit) {
1420			fprintf(stderr,
1421			    "partition %c: offset past end of unit\n", part);
1422			errors++;
1423		}
1424		if (pp->p_offset + pp->p_size > lp->d_secperunit) {
1425			fprintf(stderr,
1426			"partition %c: partition extends past end of unit\n",
1427			    part);
1428			errors++;
1429		}
1430		if (i == RAW_PART) {
1431			if (pp->p_fstype != FS_UNUSED)
1432				warnx("partition %c is not marked as unused!",part);
1433			if (pp->p_offset != 0)
1434				warnx("partition %c doesn't start at 0!",part);
1435			if (pp->p_size != lp->d_secperunit)
1436				warnx("partition %c doesn't cover the whole unit!",part);
1437
1438			if ((pp->p_fstype != FS_UNUSED) || (pp->p_offset != 0) ||
1439			    (pp->p_size != lp->d_secperunit)) {
1440				warnx("An incorrect partition %c may cause problems for "
1441				    "standard system utilities",part);
1442			}
1443		}
1444
1445		/* check for overlaps */
1446		/* this will check for all possible overlaps once and only once */
1447		for (j = 0; j < i; j++) {
1448			pp2 = &lp->d_partitions[j];
1449			if (j != RAW_PART && i != RAW_PART &&
1450			    pp->p_fstype != FS_VINUM &&
1451			    pp2->p_fstype != FS_VINUM &&
1452			    part_set[i] && part_set[j]) {
1453				if (pp2->p_offset < pp->p_offset + pp->p_size &&
1454				    (pp2->p_offset + pp2->p_size > pp->p_offset ||
1455					pp2->p_offset >= pp->p_offset)) {
1456					fprintf(stderr,"partitions %c and %c overlap!\n",
1457					    j + 'a', i + 'a');
1458					errors++;
1459				}
1460			}
1461		}
1462	}
1463	for (; i < lp->d_npartitions; i++) {
1464		part = 'a' + i;
1465		pp = &lp->d_partitions[i];
1466		if (pp->p_size || pp->p_offset)
1467			warnx("unused partition %c: size %d offset %lu",
1468			    'a' + i, pp->p_size, (u_long)pp->p_offset);
1469	}
1470	return (errors);
1471}
1472
1473/*
1474 * When operating on a "virgin" disk, try getting an initial label
1475 * from the associated device driver.  This might work for all device
1476 * drivers that are able to fetch some initial device parameters
1477 * without even having access to a (BSD) disklabel, like SCSI disks,
1478 * most IDE drives, or vn devices.
1479 *
1480 * The device name must be given in its "canonical" form.
1481 */
1482static struct disklabel *
1483getvirginlabel(void)
1484{
1485	static struct disklabel loclab;
1486	struct partition *dp;
1487	int f;
1488	u_int u;
1489
1490	if ((f = open(specname, O_RDONLY)) == -1) {
1491		warn("cannot open %s", specname);
1492		return (NULL);
1493	}
1494
1495	if (is_file)
1496		get_file_parms(f);
1497	else {
1498		mediasize = g_mediasize(f);
1499		secsize = g_sectorsize(f);
1500		if (secsize < 0 || mediasize < 0) {
1501			close (f);
1502			return (NULL);
1503		}
1504	}
1505	memset(&loclab, 0, sizeof loclab);
1506	loclab.d_magic = DISKMAGIC;
1507	loclab.d_magic2 = DISKMAGIC;
1508	loclab.d_secsize = secsize;
1509	loclab.d_secperunit = mediasize / secsize;
1510
1511	/*
1512	 * Nobody in these enligthened days uses the CHS geometry for
1513	 * anything, but nontheless try to get it right.  If we fail
1514	 * to get any good ideas from the device, construct something
1515	 * which is IBM-PC friendly.
1516	 */
1517	if (ioctl(f, DIOCGFWSECTORS, &u) == 0)
1518		loclab.d_nsectors = u;
1519	else
1520		loclab.d_nsectors = 63;
1521	if (ioctl(f, DIOCGFWHEADS, &u) == 0)
1522		loclab.d_ntracks = u;
1523	else if (loclab.d_secperunit <= 63*1*1024)
1524		loclab.d_ntracks = 1;
1525	else if (loclab.d_secperunit <= 63*16*1024)
1526		loclab.d_ntracks = 16;
1527	else
1528		loclab.d_ntracks = 255;
1529	loclab.d_secpercyl = loclab.d_ntracks * loclab.d_nsectors;
1530	loclab.d_ncylinders = loclab.d_secperunit / loclab.d_secpercyl;
1531	loclab.d_npartitions = DEFPARTITIONS;
1532
1533	/* Various (unneeded) compat stuff */
1534	loclab.d_rpm = 3600;
1535	loclab.d_bbsize = BBSIZE;
1536	loclab.d_interleave = 1;
1537	strncpy(loclab.d_typename, "amnesiac",
1538	    sizeof(loclab.d_typename));
1539
1540	dp = &loclab.d_partitions[RAW_PART];
1541	dp->p_size = loclab.d_secperunit;
1542	loclab.d_checksum = dkcksum(&loclab);
1543	close (f);
1544	return (&loclab);
1545}
1546
1547static void
1548usage(void)
1549{
1550
1551	fprintf(stderr,
1552	"%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",
1553	"usage: bsdlabel disk",
1554	"\t\t(to read label)",
1555	"	bsdlabel -w [-n] [-m machine] disk [type]",
1556	"\t\t(to write label with existing boot program)",
1557	"	bsdlabel -e [-n] [-m machine] disk",
1558	"\t\t(to edit label)",
1559	"	bsdlabel -R [-n] [-m machine] disk protofile",
1560	"\t\t(to restore label with existing boot program)",
1561	"	bsdlabel -B [-b boot] [-m machine] disk",
1562	"\t\t(to install boot program with existing on-disk label)",
1563	"	bsdlabel -w -B [-n] [-b boot] [-m machine] disk [type]",
1564	"\t\t(to write label and install boot program)",
1565	"	bsdlabel -R -B [-n] [-b boot] [-m machine] disk protofile",
1566		"\t\t(to restore label and install boot program)"
1567	);
1568	exit(1);
1569}
1570