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