sunlabel.c revision 330897
1/*-
2 * Copyright (c) 2003 Jake Burkholder.
3 * Copyright (c) 2004,2005 Joerg Wunsch.
4 * All rights reserved.
5 *
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions
8 * are met:
9 * 1. Redistributions of source code must retain the above copyright
10 *    notice, this list of conditions and the following disclaimer.
11 * 2. Redistributions in binary form must reproduce the above copyright
12 *    notice, this list of conditions and the following disclaimer in the
13 *    documentation and/or other materials provided with the distribution.
14 *
15 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
16 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
17 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
18 * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
19 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
20 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
21 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
22 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
23 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
24 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
25 * SUCH DAMAGE.
26 */
27/*-
28 * SPDX-License-Identifier: BSD-4-Clause
29 *
30 * Copyright (c) 1994, 1995 Gordon W. Ross
31 * Copyright (c) 1994 Theo de Raadt
32 * All rights reserved.
33 * Copyright (c) 1987, 1993
34 *	The Regents of the University of California.  All rights reserved.
35 *
36 * This code is derived from software contributed to Berkeley by
37 * Symmetric Computer Systems.
38 *
39 * Redistribution and use in source and binary forms, with or without
40 * modification, are permitted provided that the following conditions
41 * are met:
42 * 1. Redistributions of source code must retain the above copyright
43 *    notice, this list of conditions and the following disclaimer.
44 * 2. Redistributions in binary form must reproduce the above copyright
45 *    notice, this list of conditions and the following disclaimer in the
46 *    documentation and/or other materials provided with the distribution.
47 * 3. All advertising materials mentioning features or use of this software
48 *    must display the following acknowledgement:
49 *	This product includes software developed by the University of
50 *	California, Berkeley and its contributors.
51 *      This product includes software developed by Theo de Raadt.
52 * 4. Neither the name of the University nor the names of its contributors
53 *    may be used to endorse or promote products derived from this software
54 *    without specific prior written permission.
55 *
56 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
57 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
58 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
59 * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
60 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
61 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
62 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
63 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
64 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
65 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
66 * SUCH DAMAGE.
67 *
68 *	from: $NetBSD: disksubr.c,v 1.13 2000/12/17 22:39:18 pk $
69 */
70
71#include <sys/cdefs.h>
72__FBSDID("$FreeBSD: stable/11/sbin/sunlabel/sunlabel.c 330897 2018-03-14 03:19:51Z eadler $");
73
74#include <sys/types.h>
75#include <sys/param.h>
76#include <sys/disk.h>
77#include <sys/ioctl.h>
78#include <sys/sun_disklabel.h>
79#include <sys/wait.h>
80
81#include <ctype.h>
82#include <err.h>
83#include <fcntl.h>
84#include <inttypes.h>
85#include <libgeom.h>
86#include <paths.h>
87#include <stdio.h>
88#include <stdlib.h>
89#include <string.h>
90#include <unistd.h>
91
92#define	_PATH_TMPFILE	"/tmp/EdDk.XXXXXXXXXX"
93#define	_PATH_BOOT	"/boot/boot1"
94
95static int bflag;
96static int Bflag;
97static int cflag;
98static int eflag;
99static int hflag;
100static int nflag;
101static int Rflag;
102static int wflag;
103
104static off_t mediasize;
105static uint32_t sectorsize;
106
107struct tags {
108	const char *name;
109	unsigned int id;
110};
111
112static int check_label(struct sun_disklabel *sl);
113static void read_label(struct sun_disklabel *sl, const char *disk);
114static void write_label(struct sun_disklabel *sl, const char *disk,
115    const char *bootpath);
116static void edit_label(struct sun_disklabel *sl, const char *disk,
117    const char *bootpath);
118static int parse_label(struct sun_disklabel *sl, const char *file);
119static void print_label(struct sun_disklabel *sl, const char *disk, FILE *out);
120
121static int parse_size(struct sun_disklabel *sl, int part, char *size);
122static int parse_offset(struct sun_disklabel *sl, int part, char *offset);
123
124static const char *flagname(unsigned int tag);
125static const char *tagname(unsigned int tag);
126static unsigned int parse_flag(struct sun_disklabel *sl, int part,
127			       const char *flag);
128static unsigned int parse_tag(struct sun_disklabel *sl, int part,
129			      const char *tag);
130static const char *make_h_number(uintmax_t u);
131
132static void usage(void);
133
134extern char *__progname;
135
136static struct tags knowntags[] = {
137	{ "unassigned",	VTOC_UNASSIGNED },
138	{ "boot",	VTOC_BOOT },
139	{ "root",	VTOC_ROOT },
140	{ "swap",	VTOC_SWAP },
141	{ "usr",	VTOC_USR },
142	{ "backup",	VTOC_BACKUP },
143	{ "stand",	VTOC_STAND },
144	{ "var",	VTOC_VAR },
145	{ "home",	VTOC_HOME },
146	{ "altsctr",	VTOC_ALTSCTR },
147	{ "cache",	VTOC_CACHE },
148	{ "VxVM_pub",	VTOC_VXVM_PUB },
149	{ "VxVM_priv",	VTOC_VXVM_PRIV },
150};
151
152static struct tags knownflags[] = {
153	{ "wm", 0 },
154	{ "wu", VTOC_UNMNT },
155	{ "rm", VTOC_RONLY },
156	{ "ru", VTOC_UNMNT | VTOC_RONLY },
157};
158
159/*
160 * Disk label editor for sun disklabels.
161 */
162int
163main(int ac, char **av)
164{
165	struct sun_disklabel sl;
166	const char *bootpath;
167	const char *proto;
168	const char *disk;
169	int ch;
170
171	bootpath = _PATH_BOOT;
172	while ((ch = getopt(ac, av, "b:BcehnrRw")) != -1)
173		switch (ch) {
174		case 'b':
175			bflag = 1;
176			bootpath = optarg;
177			break;
178		case 'B':
179			Bflag = 1;
180			break;
181		case 'c':
182			cflag = 1;
183			break;
184		case 'e':
185			eflag = 1;
186			break;
187		case 'h':
188			hflag = 1;
189			break;
190		case 'n':
191			nflag = 1;
192			break;
193		case 'r':
194			fprintf(stderr, "Obsolete -r flag ignored\n");
195			break;
196		case 'R':
197			Rflag = 1;
198			break;
199		case 'w':
200			wflag = 1;
201			break;
202		default:
203			usage();
204			break;
205		}
206	if (bflag && !Bflag)
207		usage();
208	if (nflag && !(Bflag || eflag || Rflag || wflag))
209		usage();
210	if (eflag && (Rflag || wflag))
211		usage();
212	if (eflag)
213		hflag = 0;
214	ac -= optind;
215	av += optind;
216	if (ac == 0)
217		usage();
218	bzero(&sl, sizeof(sl));
219	disk = av[0];
220	if (wflag) {
221		if (ac != 2 || strcmp(av[1], "auto") != 0)
222			usage();
223		read_label(&sl, disk);
224		bzero(sl.sl_part, sizeof(sl.sl_part));
225		sl.sl_part[SUN_RAWPART].sdkp_cyloffset = 0;
226		sl.sl_part[SUN_RAWPART].sdkp_nsectors = sl.sl_ncylinders *
227		    sl.sl_ntracks * sl.sl_nsectors;
228		write_label(&sl, disk, bootpath);
229	} else if (eflag) {
230		if (ac != 1)
231			usage();
232		read_label(&sl, disk);
233		if (sl.sl_magic != SUN_DKMAGIC)
234			errx(1, "%s%s has no sun disklabel", _PATH_DEV, disk);
235		edit_label(&sl, disk, bootpath);
236	} else if (Rflag) {
237		if (ac != 2)
238			usage();
239		proto = av[1];
240		read_label(&sl, disk);
241		if (parse_label(&sl, proto) != 0)
242			errx(1, "%s: invalid label", proto);
243		write_label(&sl, disk, bootpath);
244	} else if (Bflag) {
245		read_label(&sl, disk);
246		if (sl.sl_magic != SUN_DKMAGIC)
247			errx(1, "%s%s has no sun disklabel", _PATH_DEV, disk);
248		write_label(&sl, disk, bootpath);
249	} else {
250		read_label(&sl, disk);
251		if (sl.sl_magic != SUN_DKMAGIC)
252			errx(1, "%s%s has no sun disklabel", _PATH_DEV, disk);
253		print_label(&sl, disk, stdout);
254	}
255	return (0);
256}
257
258static int
259check_label(struct sun_disklabel *sl)
260{
261	uint64_t nsectors;
262	uint64_t ostart;
263	uint64_t start;
264	uint64_t oend;
265	uint64_t end;
266	int havevtoc;
267	int warnonly;
268	int i;
269	int j;
270
271	havevtoc = sl->sl_vtoc_sane == SUN_VTOC_SANE;
272
273	nsectors = sl->sl_ncylinders * sl->sl_ntracks * sl->sl_nsectors;
274	if (sl->sl_part[SUN_RAWPART].sdkp_cyloffset != 0 ||
275	    sl->sl_part[SUN_RAWPART].sdkp_nsectors != nsectors) {
276		warnx("partition c is incorrect, must start at 0 and cover "
277		    "whole disk");
278		return (1);
279	}
280	if (havevtoc && sl->sl_vtoc_map[2].svtoc_tag != VTOC_BACKUP) {
281		warnx("partition c must have tag \"backup\"");
282		return (1);
283	}
284	for (i = 0; i < SUN_NPART; i++) {
285		if (i == 2 || sl->sl_part[i].sdkp_nsectors == 0)
286			continue;
287		start = (uint64_t)sl->sl_part[i].sdkp_cyloffset *
288		    sl->sl_ntracks * sl->sl_nsectors;
289		end = start + sl->sl_part[i].sdkp_nsectors;
290		if (end > nsectors) {
291			warnx("partition %c extends past end of disk",
292			    'a' + i);
293			return (1);
294		}
295		if (havevtoc) {
296			if (sl->sl_vtoc_map[i].svtoc_tag == VTOC_BACKUP) {
297				warnx("only partition c is allowed to have "
298				    "tag \"backup\"");
299				return (1);
300			}
301		}
302		for (j = 0; j < SUN_NPART; j++) {
303			/*
304			 * Overlaps for unmountable partitions are
305			 * non-fatal but will be warned anyway.
306			 */
307			warnonly = havevtoc &&
308				((sl->sl_vtoc_map[i].svtoc_flag & VTOC_UNMNT) != 0 ||
309				 (sl->sl_vtoc_map[j].svtoc_flag & VTOC_UNMNT) != 0);
310
311			if (j == 2 || j == i ||
312			    sl->sl_part[j].sdkp_nsectors == 0)
313				continue;
314			ostart = (uint64_t)sl->sl_part[j].sdkp_cyloffset *
315			    sl->sl_ntracks * sl->sl_nsectors;
316			oend = ostart + sl->sl_part[j].sdkp_nsectors;
317			if ((start <= ostart && end >= oend) ||
318			    (start > ostart && start < oend) ||
319			    (end > ostart && end < oend)) {
320				warnx("partition %c overlaps partition %c",
321				    'a' + i, 'a' + j);
322				if (!warnonly)
323					return (1);
324			}
325		}
326	}
327	return (0);
328}
329
330static void
331read_label(struct sun_disklabel *sl, const char *disk)
332{
333	char path[MAXPATHLEN];
334	uint32_t fwsectors;
335	uint32_t fwheads;
336	char buf[SUN_SIZE];
337	int fd, error;
338
339	snprintf(path, sizeof(path), "%s%s", _PATH_DEV, disk);
340	if ((fd = open(path, O_RDONLY)) < 0)
341		err(1, "open %s", path);
342	if (read(fd, buf, sizeof(buf)) != sizeof(buf))
343		err(1, "read");
344	error = sunlabel_dec(buf, sl);
345	if (ioctl(fd, DIOCGMEDIASIZE, &mediasize) != 0)
346		if (error)
347			err(1, "%s: ioctl(DIOCGMEDIASIZE) failed", disk);
348	if (ioctl(fd, DIOCGSECTORSIZE, &sectorsize) != 0) {
349		if (error)
350			err(1, "%s: DIOCGSECTORSIZE failed", disk);
351		else
352			sectorsize = 512;
353	}
354	if (error) {
355		bzero(sl, sizeof(*sl));
356		if (ioctl(fd, DIOCGFWSECTORS, &fwsectors) != 0)
357			fwsectors = 63;
358		if (ioctl(fd, DIOCGFWHEADS, &fwheads) != 0) {
359			if (mediasize <= 63 * 1024 * sectorsize)
360				fwheads = 1;
361			else if (mediasize <= 63 * 16 * 1024 * sectorsize)
362				fwheads = 16;
363			else
364				fwheads = 255;
365		}
366		sl->sl_rpm = 3600;
367		sl->sl_pcylinders = mediasize / (fwsectors * fwheads *
368		    sectorsize);
369		sl->sl_sparespercyl = 0;
370		sl->sl_interleave = 1;
371		sl->sl_ncylinders = sl->sl_pcylinders - 2;
372		sl->sl_acylinders = 2;
373		sl->sl_nsectors = fwsectors;
374		sl->sl_ntracks = fwheads;
375		sl->sl_part[SUN_RAWPART].sdkp_cyloffset = 0;
376		sl->sl_part[SUN_RAWPART].sdkp_nsectors = sl->sl_ncylinders *
377		    sl->sl_ntracks * sl->sl_nsectors;
378		if (mediasize > (off_t)4999L * 1024L * 1024L) {
379			sprintf(sl->sl_text,
380			    "FreeBSD%jdG cyl %u alt %u hd %u sec %u",
381			    (intmax_t)(mediasize + 512 * 1024 * 1024) /
382			        (1024 * 1024 * 1024),
383			    sl->sl_ncylinders, sl->sl_acylinders,
384			    sl->sl_ntracks, sl->sl_nsectors);
385		} else {
386			sprintf(sl->sl_text,
387			    "FreeBSD%jdM cyl %u alt %u hd %u sec %u",
388			    (intmax_t)(mediasize + 512 * 1024) / (1024 * 1024),
389			    sl->sl_ncylinders, sl->sl_acylinders,
390			    sl->sl_ntracks, sl->sl_nsectors);
391		}
392	}
393	close(fd);
394}
395
396static void
397write_label(struct sun_disklabel *sl, const char *disk, const char *bootpath)
398{
399	char path[MAXPATHLEN];
400	char boot[SUN_BOOTSIZE];
401	char buf[SUN_SIZE];
402	const char *errstr;
403	off_t off;
404	int bfd;
405	int fd;
406	int i;
407	struct gctl_req *grq;
408
409	sl->sl_magic = SUN_DKMAGIC;
410
411	if (check_label(sl) != 0)
412		errx(1, "invalid label");
413
414	bzero(buf, sizeof(buf));
415	sunlabel_enc(buf, sl);
416
417	if (nflag) {
418		print_label(sl, disk, stdout);
419		return;
420	}
421	if (Bflag) {
422		if ((bfd = open(bootpath, O_RDONLY)) < 0)
423			err(1, "open %s", bootpath);
424		i = read(bfd, boot, sizeof(boot));
425		if (i < 0)
426			err(1, "read");
427		else if (i != sizeof (boot))
428			errx(1, "read wrong size boot code (%d)", i);
429		close(bfd);
430	}
431	snprintf(path, sizeof(path), "%s%s", _PATH_DEV, disk);
432	fd = open(path, O_RDWR);
433	if (fd < 0) {
434		grq = gctl_get_handle();
435		gctl_ro_param(grq, "verb", -1, "write label");
436		gctl_ro_param(grq, "class", -1, "SUN");
437		gctl_ro_param(grq, "geom", -1, disk);
438		gctl_ro_param(grq, "label", sizeof buf, buf);
439		errstr = gctl_issue(grq);
440		if (errstr != NULL)
441			errx(1, "%s", errstr);
442		gctl_free(grq);
443		if (Bflag) {
444			grq = gctl_get_handle();
445			gctl_ro_param(grq, "verb", -1, "write bootcode");
446			gctl_ro_param(grq, "class", -1, "SUN");
447			gctl_ro_param(grq, "geom", -1, disk);
448			gctl_ro_param(grq, "bootcode", sizeof boot, boot);
449			errstr = gctl_issue(grq);
450			if (errstr != NULL)
451				errx(1, "%s", errstr);
452			gctl_free(grq);
453		}
454	} else {
455		if (lseek(fd, 0, SEEK_SET) < 0)
456			err(1, "lseek");
457		if (write(fd, buf, sizeof(buf)) != sizeof(buf))
458			err (1, "write");
459		if (Bflag) {
460			for (i = 0; i < SUN_NPART; i++) {
461				if (sl->sl_part[i].sdkp_nsectors == 0)
462					continue;
463				off = sl->sl_part[i].sdkp_cyloffset *
464				    sl->sl_ntracks * sl->sl_nsectors * 512;
465				/*
466				 * Ignore first SUN_SIZE bytes of boot code to
467				 * avoid overwriting the label.
468				 */
469				if (lseek(fd, off + SUN_SIZE, SEEK_SET) < 0)
470					err(1, "lseek");
471				if (write(fd, boot + SUN_SIZE,
472				    sizeof(boot) - SUN_SIZE) !=
473				    sizeof(boot) - SUN_SIZE)
474					err(1, "write");
475			}
476		}
477		close(fd);
478	}
479	exit(0);
480}
481
482static void
483edit_label(struct sun_disklabel *sl, const char *disk, const char *bootpath)
484{
485	char tmpfil[] = _PATH_TMPFILE;
486	const char *editor;
487	int status;
488	FILE *fp;
489	pid_t pid;
490	pid_t r;
491	int fd;
492	int c;
493
494	if ((fd = mkstemp(tmpfil)) < 0)
495		err(1, "mkstemp");
496	if ((fp = fdopen(fd, "w")) == NULL)
497		err(1, "fdopen");
498	print_label(sl, disk, fp);
499	fflush(fp);
500	for (;;) {
501		if ((pid = fork()) < 0)
502			err(1, "fork");
503		if (pid == 0) {
504			if ((editor = getenv("EDITOR")) == NULL)
505				editor = _PATH_VI;
506			execlp(editor, editor, tmpfil, (char *)NULL);
507			err(1, "execlp %s", editor);
508		}
509		status = 0;
510		while ((r = wait(&status)) > 0 && r != pid)
511			;
512		if (WIFEXITED(status)) {
513			if (parse_label(sl, tmpfil) == 0) {
514				fclose(fp);
515				unlink(tmpfil);
516				write_label(sl, disk, bootpath);
517				return;
518			}
519			printf("re-edit the label? [y]: ");
520			fflush(stdout);
521			c = getchar();
522			if (c != EOF && c != '\n')
523				while (getchar() != '\n')
524					;
525			if  (c == 'n') {
526				fclose(fp);
527				unlink(tmpfil);
528				return;
529			}
530		}
531	}
532	fclose(fp);
533	unlink(tmpfil);
534	return;
535}
536
537static int
538parse_label(struct sun_disklabel *sl, const char *file)
539{
540	char offset[32];
541	char size[32];
542	char flag[32];
543	char tag[32];
544	char buf[128];
545	char text[128];
546	char volname[SUN_VOLNAME_LEN + 1];
547	struct sun_disklabel sl1;
548	char *bp;
549	const char *what;
550	uint8_t part;
551	FILE *fp;
552	int line;
553	int rv;
554	int wantvtoc;
555	unsigned alt, cyl, hd, nr, sec;
556
557	line = wantvtoc = 0;
558	if ((fp = fopen(file, "r")) == NULL)
559		err(1, "fopen");
560	sl1 = *sl;
561	bzero(&sl1.sl_part, sizeof(sl1.sl_part));
562	while (fgets(buf, sizeof(buf), fp) != NULL) {
563		/*
564		 * In order to recognize a partition entry, we search
565		 * for lines starting with a single letter followed by
566		 * a colon as their first non-white characters.  We
567		 * silently ignore any other lines, so any comment etc.
568		 * lines in the label template will be ignored.
569		 *
570		 * XXX We should probably also recognize the geometry
571		 * fields on top, and allow changing the geometry
572		 * emulated by this disk.
573		 */
574		for (bp = buf; isspace(*bp); bp++)
575			;
576		if (strncmp(bp, "text:", strlen("text:")) == 0) {
577			bp += strlen("text:");
578			rv = sscanf(bp,
579			    " %s cyl %u alt %u hd %u sec %u",
580			    text, &cyl, &alt, &hd, &sec);
581			if (rv != 5) {
582				warnx("%s, line %d: text label does not "
583				    "contain required fields",
584				    file, line + 1);
585				fclose(fp);
586				return (1);
587			}
588			if (alt != 2) {
589				warnx("%s, line %d: # alt must be equal 2",
590				    file, line + 1);
591				fclose(fp);
592				return (1);
593			}
594			if (cyl == 0 || cyl > USHRT_MAX) {
595				what = "cyl";
596				nr = cyl;
597			unreasonable:
598				warnx("%s, line %d: # %s %d unreasonable",
599				    file, line + 1, what, nr);
600				fclose(fp);
601				return (1);
602			}
603			if (hd == 0 || hd > USHRT_MAX) {
604				what = "hd";
605				nr = hd;
606				goto unreasonable;
607			}
608			if (sec == 0 || sec > USHRT_MAX) {
609				what = "sec";
610				nr = sec;
611				goto unreasonable;
612			}
613			if (mediasize == 0)
614				warnx("unit size unknown, no sector count "
615				    "check could be done");
616			else if ((uintmax_t)(cyl + alt) * sec * hd >
617				 (uintmax_t)mediasize / sectorsize) {
618				warnx("%s, line %d: sector count %ju exceeds "
619				    "unit size %ju",
620				    file, line + 1,
621				    (uintmax_t)(cyl + alt) * sec * hd,
622				    (uintmax_t)mediasize / sectorsize);
623				fclose(fp);
624				return (1);
625			}
626			sl1.sl_pcylinders = cyl + alt;
627			sl1.sl_ncylinders = cyl;
628			sl1.sl_acylinders = alt;
629			sl1.sl_nsectors = sec;
630			sl1.sl_ntracks = hd;
631			memset(sl1.sl_text, 0, sizeof(sl1.sl_text));
632			snprintf(sl1.sl_text, sizeof(sl1.sl_text),
633			    "%s cyl %u alt %u hd %u sec %u",
634			    text, cyl, alt, hd, sec);
635			continue;
636		}
637		if (strncmp(bp, "volume name:", strlen("volume name:")) == 0) {
638			wantvtoc = 1; /* Volume name requires VTOC. */
639			bp += strlen("volume name:");
640#if SUN_VOLNAME_LEN != 8
641# error "scanf field width does not match SUN_VOLNAME_LEN"
642#endif
643			/*
644			 * We set the field length to one more than
645			 * SUN_VOLNAME_LEN to allow detecting an
646			 * overflow.
647			 */
648			memset(volname, 0, sizeof volname);
649			rv = sscanf(bp, " %9[^\n]", volname);
650			if (rv != 1) {
651				/* Clear the volume name. */
652				memset(sl1.sl_vtoc_volname, 0,
653				    SUN_VOLNAME_LEN);
654			} else {
655				memcpy(sl1.sl_vtoc_volname, volname,
656				    SUN_VOLNAME_LEN);
657				if (volname[SUN_VOLNAME_LEN] != '\0')
658					warnx(
659"%s, line %d: volume name longer than %d characters, truncating",
660					    file, line + 1, SUN_VOLNAME_LEN);
661			}
662			continue;
663		}
664		if (strlen(bp) < 2 || bp[1] != ':') {
665			line++;
666			continue;
667		}
668		rv = sscanf(bp, "%c: %30s %30s %30s %30s",
669		    &part, size, offset, tag, flag);
670		if (rv < 3) {
671		syntaxerr:
672			warnx("%s: syntax error on line %d",
673			    file, line + 1);
674			fclose(fp);
675			return (1);
676		}
677		if (parse_size(&sl1, part - 'a', size) ||
678		    parse_offset(&sl1, part - 'a', offset))
679			goto syntaxerr;
680		if (rv > 3) {
681			wantvtoc = 1;
682			if (rv == 5 && parse_flag(&sl1, part - 'a', flag))
683				goto syntaxerr;
684			if (parse_tag(&sl1, part - 'a', tag))
685				goto syntaxerr;
686		}
687		line++;
688	}
689	fclose(fp);
690	if (wantvtoc) {
691		sl1.sl_vtoc_sane = SUN_VTOC_SANE;
692		sl1.sl_vtoc_vers = SUN_VTOC_VERSION;
693		sl1.sl_vtoc_nparts = SUN_NPART;
694	} else {
695		sl1.sl_vtoc_sane = 0;
696		sl1.sl_vtoc_vers = 0;
697		sl1.sl_vtoc_nparts = 0;
698		bzero(&sl1.sl_vtoc_map, sizeof(sl1.sl_vtoc_map));
699	}
700	*sl = sl1;
701	return (check_label(sl));
702}
703
704static int
705parse_size(struct sun_disklabel *sl, int part, char *size)
706{
707	uintmax_t nsectors;
708	uintmax_t total;
709	uintmax_t n;
710	char *p;
711	int i;
712
713	nsectors = 0;
714	n = strtoumax(size, &p, 10);
715	if (*p != '\0') {
716		if (strcmp(size, "*") == 0) {
717			total = sl->sl_ncylinders * sl->sl_ntracks *
718			    sl->sl_nsectors;
719			for (i = 0; i < part; i++) {
720				if (i == 2)
721					continue;
722				nsectors += sl->sl_part[i].sdkp_nsectors;
723			}
724			n = total - nsectors;
725		} else if (p[1] == '\0' && (p[0] == 'C' || p[0] == 'c')) {
726			n = n * sl->sl_ntracks * sl->sl_nsectors;
727		} else if (p[1] == '\0' && (p[0] == 'K' || p[0] == 'k')) {
728			n = roundup((n * 1024) / 512,
729			    sl->sl_ntracks * sl->sl_nsectors);
730		} else if (p[1] == '\0' && (p[0] == 'M' || p[0] == 'm')) {
731			n = roundup((n * 1024 * 1024) / 512,
732			    sl->sl_ntracks * sl->sl_nsectors);
733		} else if (p[1] == '\0' && (p[0] == 'S' || p[0] == 's')) {
734			/* size in sectors, no action neded */
735		} else if (p[1] == '\0' && (p[0] == 'G' || p[0] == 'g')) {
736			n = roundup((n * 1024 * 1024 * 1024) / 512,
737			    sl->sl_ntracks * sl->sl_nsectors);
738		} else
739			return (-1);
740	} else if (cflag) {
741		n = n * sl->sl_ntracks * sl->sl_nsectors;
742	}
743	sl->sl_part[part].sdkp_nsectors = n;
744	return (0);
745}
746
747static int
748parse_offset(struct sun_disklabel *sl, int part, char *offset)
749{
750	uintmax_t nsectors;
751	uintmax_t n;
752	char *p;
753	int i;
754
755	nsectors = 0;
756	n = strtoumax(offset, &p, 10);
757	if (*p != '\0') {
758		if (strcmp(offset, "*") == 0) {
759			for (i = 0; i < part; i++) {
760				if (i == 2)
761					continue;
762				nsectors += sl->sl_part[i].sdkp_nsectors;
763			}
764			n = nsectors / (sl->sl_nsectors * sl->sl_ntracks);
765		} else
766			return (-1);
767	}
768	sl->sl_part[part].sdkp_cyloffset = n;
769	return (0);
770}
771
772static void
773print_label(struct sun_disklabel *sl, const char *disk, FILE *out)
774{
775	int i, j;
776	int havevtoc;
777	uintmax_t secpercyl;
778	/* Long enough to hex-encode each character. */
779	char volname[4 * SUN_VOLNAME_LEN + 1];
780
781	havevtoc = sl->sl_vtoc_sane == SUN_VTOC_SANE;
782	secpercyl = sl->sl_nsectors * sl->sl_ntracks;
783
784	fprintf(out,
785"# /dev/%s:\n"
786"text: %s\n"
787"bytes/sector: %d\n"
788"sectors/cylinder: %ju\n",
789	    disk,
790	    sl->sl_text,
791	    sectorsize,
792	    secpercyl);
793	if (eflag)
794		fprintf(out,
795		    "# max sectors/unit (including alt cylinders): %ju\n",
796		    (uintmax_t)mediasize / sectorsize);
797	fprintf(out,
798"sectors/unit: %ju\n",
799	    secpercyl * sl->sl_ncylinders);
800	if (havevtoc && sl->sl_vtoc_volname[0] != '\0') {
801		for (i = j = 0; i < SUN_VOLNAME_LEN; i++) {
802			if (sl->sl_vtoc_volname[i] == '\0')
803				break;
804			if (isprint(sl->sl_vtoc_volname[i]))
805				volname[j++] = sl->sl_vtoc_volname[i];
806			else
807				j += sprintf(volname + j, "\\x%02X",
808				    sl->sl_vtoc_volname[i]);
809		}
810		volname[j] = '\0';
811		fprintf(out, "volume name: %s\n", volname);
812	}
813	fprintf(out,
814"\n"
815"%d partitions:\n"
816"#\n",
817	    SUN_NPART);
818	if (!hflag) {
819		fprintf(out, "# Size is in %s.", cflag? "cylinders": "sectors");
820		if (eflag)
821			fprintf(out,
822"  Use %%d%c, %%dK, %%dM or %%dG to specify in %s,\n"
823"# kilobytes, megabytes or gigabytes respectively, or '*' to specify rest of\n"
824"# disk.\n",
825			    cflag? 's': 'c',
826			    cflag? "sectors": "cylinders");
827		else
828			putc('\n', out);
829		fprintf(out, "# Offset is in cylinders.");
830		if (eflag)
831			fprintf(out,
832"  Use '*' to calculate offsets automatically.\n"
833"#\n");
834		else
835			putc('\n', out);
836	}
837	if (havevtoc)
838		fprintf(out,
839"#    size       offset      tag         flag\n"
840"#    ---------- ----------  ----------  ----\n"
841			);
842	else
843		fprintf(out,
844"#    size       offset\n"
845"#    ---------- ----------\n"
846			);
847
848	for (i = 0; i < SUN_NPART; i++) {
849		if (sl->sl_part[i].sdkp_nsectors == 0)
850			continue;
851		if (hflag) {
852			fprintf(out, "  %c: %10s",
853			    'a' + i,
854			    make_h_number((uintmax_t)
855				sl->sl_part[i].sdkp_nsectors * 512));
856			fprintf(out, " %10s",
857			    make_h_number((uintmax_t)
858				sl->sl_part[i].sdkp_cyloffset * 512
859				* secpercyl));
860		} else {
861			fprintf(out, "  %c: %10ju %10u",
862			    'a' + i,
863			    sl->sl_part[i].sdkp_nsectors / (cflag? secpercyl: 1),
864			    sl->sl_part[i].sdkp_cyloffset);
865		}
866		if (havevtoc)
867			fprintf(out, " %11s %5s",
868			    tagname(sl->sl_vtoc_map[i].svtoc_tag),
869			    flagname(sl->sl_vtoc_map[i].svtoc_flag));
870		putc('\n', out);
871	}
872}
873
874static void
875usage(void)
876{
877
878	fprintf(stderr, "usage:"
879"\t%s [-r] [-c | -h] disk\n"
880"\t\t(to read label)\n"
881"\t%s -B [-b boot1] [-n] disk\n"
882"\t\t(to install boot program only)\n"
883"\t%s -R [-B [-b boot1]] [-r] [-n] [-c] disk protofile\n"
884"\t\t(to restore label)\n"
885"\t%s -e [-B [-b boot1]] [-r] [-n] [-c] disk\n"
886"\t\t(to edit label)\n"
887"\t%s -w [-B [-b boot1]] [-r] [-n] disk type\n"
888"\t\t(to write default label)\n",
889	     __progname,
890	     __progname,
891	     __progname,
892	     __progname,
893	     __progname);
894	exit(1);
895}
896
897/*
898 * Return VTOC tag and flag names for tag or flag ID, resp.
899 */
900static const char *
901tagname(unsigned int tag)
902{
903	static char buf[32];
904	size_t i;
905	struct tags *tp;
906
907	for (i = 0, tp = knowntags; i < nitems(knowntags); i++, tp++)
908		if (tp->id == tag)
909			return (tp->name);
910
911	sprintf(buf, "%u", tag);
912
913	return (buf);
914}
915
916static const char *
917flagname(unsigned int flag)
918{
919	static char buf[32];
920	size_t i;
921	struct tags *tp;
922
923	for (i = 0, tp = knownflags; i < nitems(knownflags); i++, tp++)
924		if (tp->id == flag)
925			return (tp->name);
926
927	sprintf(buf, "%u", flag);
928
929	return (buf);
930}
931
932static unsigned int
933parse_tag(struct sun_disklabel *sl, int part, const char *tag)
934{
935	struct tags *tp;
936	char *endp;
937	size_t i;
938	unsigned long l;
939
940	for (i = 0, tp = knowntags; i < nitems(knowntags); i++, tp++)
941		if (strcmp(tp->name, tag) == 0) {
942			sl->sl_vtoc_map[part].svtoc_tag = (uint16_t)tp->id;
943			return (0);
944		}
945
946	l = strtoul(tag, &endp, 0);
947	if (*tag != '\0' && *endp == '\0') {
948		sl->sl_vtoc_map[part].svtoc_tag = (uint16_t)l;
949		return (0);
950	}
951
952	return (-1);
953}
954
955static unsigned int
956parse_flag(struct sun_disklabel *sl, int part, const char *flag)
957{
958	struct tags *tp;
959	char *endp;
960	size_t i;
961	unsigned long l;
962
963	for (i = 0, tp = knownflags; i < nitems(knownflags); i++, tp++)
964		if (strcmp(tp->name, flag) == 0) {
965			sl->sl_vtoc_map[part].svtoc_flag = (uint16_t)tp->id;
966			return (0);
967		}
968
969	l = strtoul(flag, &endp, 0);
970	if (*flag != '\0' && *endp == '\0') {
971		sl->sl_vtoc_map[part].svtoc_flag = (uint16_t)l;
972		return (0);
973	}
974
975	return (-1);
976}
977
978/*
979 * Convert argument into `human readable' byte number form.
980 */
981static const char *
982make_h_number(uintmax_t u)
983{
984	static char buf[32];
985	double d;
986
987	if (u == 0) {
988		strcpy(buf, "0B");
989	} else if (u > 2000000000UL) {
990		d = (double)u / 1e9;
991		sprintf(buf, "%.1fG", d);
992	} else if (u > 2000000UL) {
993		d = (double)u / 1e6;
994		sprintf(buf, "%.1fM", d);
995	} else {
996		d = (double)u / 1e3;
997		sprintf(buf, "%.1fK", d);
998	}
999
1000	return (buf);
1001}
1002