sunlabel.c revision 110130
1/*-
2 * Copyright (c) 2003 Jake Burkholder.
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 * 1. Redistributions of source code must retain the above copyright
9 *    notice, this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright
11 *    notice, this list of conditions and the following disclaimer in the
12 *    documentation and/or other materials provided with the distribution.
13 *
14 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17 * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24 * SUCH DAMAGE.
25 */
26/*
27 * Copyright (c) 1994, 1995 Gordon W. Ross
28 * Copyright (c) 1994 Theo de Raadt
29 * All rights reserved.
30 * Copyright (c) 1987, 1993
31 *	The Regents of the University of California.  All rights reserved.
32 *
33 * This code is derived from software contributed to Berkeley by
34 * Symmetric Computer Systems.
35 *
36 * Redistribution and use in source and binary forms, with or without
37 * modification, are permitted provided that the following conditions
38 * are met:
39 * 1. Redistributions of source code must retain the above copyright
40 *    notice, this list of conditions and the following disclaimer.
41 * 2. Redistributions in binary form must reproduce the above copyright
42 *    notice, this list of conditions and the following disclaimer in the
43 *    documentation and/or other materials provided with the distribution.
44 * 3. All advertising materials mentioning features or use of this software
45 *    must display the following acknowledgement:
46 *	This product includes software developed by the University of
47 *	California, Berkeley and its contributors.
48 *      This product includes software developed by Theo de Raadt.
49 * 4. Neither the name of the University nor the names of its contributors
50 *    may be used to endorse or promote products derived from this software
51 *    without specific prior written permission.
52 *
53 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
54 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
55 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
56 * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
57 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
58 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
59 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
60 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
61 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
62 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
63 * SUCH DAMAGE.
64 *
65 *	from: $NetBSD: disksubr.c,v 1.13 2000/12/17 22:39:18 pk $
66 */
67
68#include <sys/cdefs.h>
69__FBSDID("$FreeBSD: head/sbin/sunlabel/sunlabel.c 110130 2003-01-31 07:03:25Z jake $");
70
71#include <sys/types.h>
72#include <sys/param.h>
73#include <sys/sysctl.h>
74#include <sys/sun_disklabel.h>
75#include <sys/wait.h>
76
77#include <err.h>
78#include <fcntl.h>
79#include <inttypes.h>
80#include <paths.h>
81#include <stdio.h>
82#include <stdlib.h>
83#include <string.h>
84#include <unistd.h>
85
86#define	_PATH_TMPFILE	"/tmp/EdDk.XXXXXXXXXX"
87#define	_PATH_BOOT	"/boot/boot1"
88
89static int bflag;
90static int Bflag;
91static int eflag;
92static int nflag;
93static int rflag = 1;
94static int Rflag;
95static int wflag;
96
97static int check_label(struct sun_disklabel *sl);
98static void read_label(struct sun_disklabel *sl, const char *disk);
99static void write_label(struct sun_disklabel *sl, const char *disk,
100    const char *bootpath);
101static int edit_label(struct sun_disklabel *sl, const char *disk,
102    const char *bootpath);
103static int parse_label(struct sun_disklabel *sl, const char *file);
104static void print_label(struct sun_disklabel *sl, const char *disk, FILE *out);
105
106static int parse_size(struct sun_disklabel *sl, int part, char *size);
107static int parse_offset(struct sun_disklabel *sl, int part, char *offset);
108
109static void usage(void);
110
111extern char *__progname;
112
113/*
114 * Disk label editor for sun disklabels.
115 */
116int
117main(int ac, char **av)
118{
119	struct sun_disklabel sl;
120	const char *bootpath;
121	const char *proto;
122	const char *disk;
123	int ch;
124
125	bootpath = _PATH_BOOT;
126	while ((ch = getopt(ac, av, "b:BenrRw")) != -1)
127		switch (ch) {
128		case 'b':
129			bflag = 1;
130			bootpath = optarg;
131			break;
132		case 'B':
133			Bflag = 1;
134			break;
135		case 'e':
136			eflag = 1;
137			break;
138		case 'n':
139			nflag = 1;
140			break;
141		case 'r':
142			rflag = 1;
143			break;
144		case 'R':
145			Rflag = 1;
146			break;
147		case 'w':
148			wflag = 1;
149			break;
150		default:
151			usage();
152			break;
153		}
154	if (bflag && !Bflag)
155		usage();
156	if (nflag && !(Bflag || eflag || Rflag || wflag))
157		usage();
158	if (eflag && (Rflag || wflag))
159		usage();
160	ac -= optind;
161	av += optind;
162	if (ac == 0)
163		usage();
164	bzero(&sl, sizeof(sl));
165	disk = av[0];
166	if (wflag) {
167		if (ac != 2 || strcmp(av[1], "auto") != 0)
168			usage();
169		read_label(&sl, disk);
170		bzero(sl.sl_part, sizeof(sl.sl_part));
171		sl.sl_part[2].sdkp_cyloffset = 0;
172		sl.sl_part[2].sdkp_nsectors = sl.sl_ncylinders *
173		    sl.sl_ntracks * sl.sl_nsectors;
174		write_label(&sl, disk, bootpath);
175	} else if (eflag) {
176		if (ac != 1)
177			usage();
178		read_label(&sl, disk);
179		if (sl.sl_magic != SUN_DKMAGIC)
180			errx(1, "%s%s has no sun disklabel", _PATH_DEV, disk);
181		while (edit_label(&sl, disk, bootpath) != 0)
182			;
183	} else if (Rflag) {
184		if (ac != 2)
185			usage();
186		proto = av[1];
187		read_label(&sl, disk);
188		write_label(&sl, disk, bootpath);
189	} else if (Bflag) {
190		read_label(&sl, disk);
191		if (sl.sl_magic != SUN_DKMAGIC)
192			errx(1, "%s%s has no sun disklabel", _PATH_DEV, disk);
193		write_label(&sl, disk, bootpath);
194	} else {
195		read_label(&sl, disk);
196		if (sl.sl_magic != SUN_DKMAGIC)
197			errx(1, "%s%s has no sun disklabel", _PATH_DEV, disk);
198		print_label(&sl, disk, stdout);
199	}
200	return (0);
201}
202
203static int
204check_label(struct sun_disklabel *sl)
205{
206	uint64_t nsectors;
207	uint64_t ostart;
208	uint64_t start;
209	uint64_t oend;
210	uint64_t end;
211	int i;
212	int j;
213
214	nsectors = sl->sl_ncylinders * sl->sl_ntracks * sl->sl_nsectors;
215	for (i = 0; i < 8; i++) {
216		if (i == 2 || sl->sl_part[i].sdkp_nsectors == 0)
217			continue;
218		start = (uint64_t)sl->sl_part[i].sdkp_cyloffset *
219		    sl->sl_ntracks * sl->sl_nsectors;
220		end = start + sl->sl_part[i].sdkp_nsectors;
221		if (end > nsectors) {
222			warnx("partition %c extends past end of disk",
223			    'a' + i);
224			return (1);
225		}
226		for (j = 0; j < 8; j++) {
227			if (j == 2 || j == i ||
228			    sl->sl_part[j].sdkp_nsectors == 0)
229				continue;
230			ostart = (uint64_t)sl->sl_part[j].sdkp_cyloffset *
231			    sl->sl_ntracks * sl->sl_nsectors;
232			oend = ostart + sl->sl_part[j].sdkp_nsectors;
233			if ((start <= ostart && end >= oend) ||
234			    (start > ostart && start < oend) ||
235			    (end > ostart && end < oend)) {
236				warnx("partition %c overlaps partition %c",
237				    'a' + i, 'a' + j);
238				return (1);
239			}
240		}
241	}
242	return (0);
243}
244
245static void
246read_label(struct sun_disklabel *sl, const char *disk)
247{
248	uint32_t bytepercyl;
249	uint32_t bytepersec;
250	uint32_t acylinders;
251	uint32_t nsectors;
252	uint32_t ntracks;
253	uintmax_t offset;
254	uintmax_t size;
255	char name[64];
256	char *text;
257	char *p, *s;
258	size_t len;
259	int i;
260	int n;
261
262	if (sysctlbyname("kern.geom.conftxt", NULL, &len, NULL, 0) < 0)
263		err(1, "sysctlbyname");
264	if ((text = malloc(len)) == NULL)
265		err(1, "malloc");
266	if (sysctlbyname("kern.geom.conftxt", text, &len, NULL, 0) < 0)
267		err(1, "sysctlbyname");
268	bytepercyl = 0;
269	for (p = text; (s = strsep(&p, "\n")) != NULL;) {
270		n = sscanf(s, "%*u DISK %s %ju %u hd %u sc %u\n", name, &size,
271		    &bytepersec, &ntracks, &nsectors);
272		if (n == 5) {
273			if (strncmp(name, disk, strlen(disk)) != 0)
274				continue;
275			bytepercyl = ntracks * nsectors * bytepersec;
276			sl->sl_pcylinders = size / bytepercyl;
277			sl->sl_acylinders = 2;
278			sl->sl_nsectors = nsectors;
279			sl->sl_ntracks = ntracks;
280			continue;
281		}
282		n = sscanf(s,
283		    "%*u SUN %s %ju %*u i %u o %ju sc %*u hd %*u alt %u",
284		    name, &size, &i, &offset, &acylinders);
285		if (n == 5) {
286			if (strncmp(name, disk, strlen(disk)) != 0)
287				continue;
288			sl->sl_acylinders = acylinders;
289			sl->sl_part[i].sdkp_cyloffset = offset / bytepercyl;
290			sl->sl_part[i].sdkp_nsectors = size / bytepersec;
291			sl->sl_magic = SUN_DKMAGIC;
292			continue;
293		}
294	}
295	if (bytepercyl == 0)
296		errx(1, "disk %s not found", disk);
297	sl->sl_ncylinders = sl->sl_pcylinders -
298	    sl->sl_acylinders;
299	sl->sl_interleave = 1;
300	sl->sl_sparespercyl = 0;
301	sl->sl_rpm = 3600;
302	size = (uint64_t)sl->sl_ncylinders * sl->sl_ntracks * sl->sl_nsectors;
303	if (size > 4999 * 1024 * 2) {
304		sprintf(sl->sl_text, "FreeBSD%luG cyl %u alt %u hd %u sec %u",
305		    (size + 1024 * 1024) / (2 * 1024 * 1024),
306		    sl->sl_ncylinders, sl->sl_acylinders,
307		    sl->sl_ntracks, sl->sl_nsectors);
308	} else {
309		sprintf(sl->sl_text, "FreeBSD%luM cyl %u alt %u hd %u sec %u",
310		    (size + 1024) / (2 * 1024),
311		    sl->sl_ncylinders, sl->sl_acylinders,
312		    sl->sl_ntracks, sl->sl_nsectors);
313	}
314}
315
316static void
317write_label(struct sun_disklabel *sl, const char *disk, const char *bootpath)
318{
319	char path[MAXPATHLEN];
320	char boot[16 * 512];
321	uint16_t cksum;
322	uint16_t *sp1;
323	uint16_t *sp2;
324	off_t off;
325	int bfd;
326	int fd;
327	int i;
328
329	sl->sl_magic = SUN_DKMAGIC;
330
331	sp1 = (u_short *)sl;
332	sp2 = (u_short *)(sl + 1);
333	cksum = 0;
334	while (sp1 < sp2)
335		cksum ^= *sp1++;
336	sl->sl_cksum = cksum;
337
338	if (check_label(sl) != 0)
339		errx(1, "invalid label");
340
341	if (nflag) {
342		print_label(sl, disk, stdout);
343	} else if (rflag) {
344		snprintf(path, sizeof(path), "%s%s", _PATH_DEV, disk);
345		if ((fd = open(path, O_RDWR)) < 0)
346			err(1, "open %s", path);
347		if (Bflag) {
348			if ((bfd = open(bootpath, O_RDONLY)) < 0)
349				err(1, "open %s", bootpath);
350			if (read(bfd, boot, sizeof(boot)) != sizeof(boot))
351				err(1, "read");
352			close(bfd);
353			for (i = 0; i < 8; i++) {
354				if (sl->sl_part[i].sdkp_nsectors == 0)
355					continue;
356				off = sl->sl_part[i].sdkp_cyloffset *
357				    sl->sl_ntracks * sl->sl_nsectors * 512;
358				if (lseek(fd, off, SEEK_SET) < 0)
359					err(1, "lseek");
360				if (write(fd, boot, sizeof(boot)) !=
361				    sizeof(boot))
362					err(1, "write");
363			}
364		}
365		if (lseek(fd, 0, SEEK_SET) < 0)
366			err(1, "lseek");
367		if (write(fd, sl, sizeof(*sl)) != sizeof(*sl))
368			err(1, "write");
369		close(fd);
370	} else
371		err(1, "implement!");
372}
373
374static int
375edit_label(struct sun_disklabel *sl, const char *disk, const char *bootpath)
376{
377	char tmpfil[] = _PATH_TMPFILE;
378	const char *editor;
379	int status;
380	FILE *fp;
381	pid_t pid;
382	pid_t r;
383	int fd;
384	int c;
385
386	if ((fd = mkstemp(tmpfil)) < 0)
387		err(1, "mkstemp");
388	if ((fp = fdopen(fd, "w")) == NULL)
389		err(1, "fdopen");
390	print_label(sl, disk, fp);
391	fflush(fp);
392	if ((pid = fork()) < 0)
393		err(1, "fork");
394	if (pid == 0) {
395		if ((editor = getenv("EDITOR")) == NULL)
396			editor = _PATH_VI;
397		execlp(editor, editor, tmpfil, NULL);
398		err(1, "execlp %s", editor);
399	}
400	status = 0;
401	while ((r = wait(&status)) > 0 && r != pid)
402		;
403	if (WIFEXITED(status)) {
404		if (parse_label(sl, tmpfil) == 0) {
405			fclose(fp);
406			unlink(tmpfil);
407			write_label(sl, disk, bootpath);
408			return (0);
409		}
410		printf("re-edit the label? [y]: ");
411		fflush(stdout);
412		c = getchar();
413		if (c != EOF && c != '\n')
414			while (getchar() != '\n')
415				;
416		if  (c == 'n') {
417			fclose(fp);
418			unlink(tmpfil);
419			return (0);
420		}
421	}
422	fclose(fp);
423	unlink(tmpfil);
424	return (1);
425}
426
427static int
428parse_label(struct sun_disklabel *sl, const char *file)
429{
430	char offset[32];
431	char size[32];
432	char buf[128];
433	uint8_t part;
434	FILE *fp;
435	int line;
436
437	line = 0;
438	if ((fp = fopen(file, "r")) == NULL)
439		err(1, "fopen");
440	bzero(sl->sl_part, sizeof(sl->sl_part));
441	while (fgets(buf, sizeof(buf), fp) != NULL) {
442		if (buf[0] != ' ' || buf[1] != ' ')
443			continue;
444		if (sscanf(buf, "  %c: %s %s\n", &part, size, offset) != 3 ||
445		    parse_size(sl, part - 'a', size) ||
446		    parse_offset(sl, part - 'a', offset)) {
447			warnx("%s: syntex error on line %d",
448			    file, line);
449			fclose(fp);
450			return (1);
451		}
452		line++;
453	}
454	fclose(fp);
455	sl->sl_part[2].sdkp_cyloffset = 0;
456	sl->sl_part[2].sdkp_nsectors = sl->sl_ncylinders *
457	    sl->sl_ntracks * sl->sl_nsectors;
458	return (check_label(sl));
459}
460
461static int
462parse_size(struct sun_disklabel *sl, int part, char *size)
463{
464	uintmax_t nsectors;
465	uintmax_t total;
466	uintmax_t n;
467	char *p;
468	int i;
469
470	nsectors = 0;
471	n = strtoumax(size, &p, 10);
472	if (*p != '\0') {
473		if (strcmp(size, "*") == 0) {
474			total = sl->sl_ncylinders * sl->sl_ntracks *
475			    sl->sl_nsectors;
476			for (i = 0; i < part; i++) {
477				if (i == 2)
478					continue;
479				nsectors += sl->sl_part[i].sdkp_nsectors;
480			}
481			n = total - nsectors;
482		} else if (p[1] == '\0' && (p[0] == 'K' || p[0] == 'k')) {
483			n = roundup((n * 1024) / 512,
484			    sl->sl_ntracks * sl->sl_nsectors);
485		} else if (p[1] == '\0' && (p[0] == 'M' || p[0] == 'm')) {
486			n = roundup((n * 1024 * 1024) / 512,
487			    sl->sl_ntracks * sl->sl_nsectors);
488		} else if (p[1] == '\0' && (p[0] == 'G' || p[0] == 'g')) {
489			n = roundup((n * 1024 * 1024 * 1024) / 512,
490			    sl->sl_ntracks * sl->sl_nsectors);
491		} else
492			return (-1);
493	}
494	sl->sl_part[part].sdkp_nsectors = n;
495	return (0);
496}
497
498static int
499parse_offset(struct sun_disklabel *sl, int part, char *offset)
500{
501	uintmax_t nsectors;
502	uintmax_t n;
503	char *p;
504	int i;
505
506	nsectors = 0;
507	n = strtoumax(offset, &p, 10);
508	if (*p != '\0') {
509		if (strcmp(offset, "*") == 0) {
510			for (i = 0; i < part; i++) {
511				if (i == 2)
512					continue;
513				nsectors += sl->sl_part[i].sdkp_nsectors;
514			}
515			n = nsectors / (sl->sl_nsectors * sl->sl_ntracks);
516		} else
517			return (-1);
518	}
519	sl->sl_part[part].sdkp_cyloffset = n;
520	return (0);
521}
522
523static void
524print_label(struct sun_disklabel *sl, const char *disk, FILE *out)
525{
526	int i;
527
528	fprintf(out,
529"# /dev/%s:\n"
530"text: %s\n"
531"bytes/sectors: 512\n"
532"sectors/cylinder: %d\n"
533"sectors/unit: %d\n"
534"\n"
535"8 partitions:\n"
536"#\n"
537"# Size is in sectors, use %%dK, %%dM or %%dG to specify in kilobytes,\n"
538"# megabytes or gigabytes respectively, or '*' to specify rest of disk.\n"
539"# Offset is in cylinders, use '*' to calculate offsets automatically.\n"
540"#\n"
541"#    size       offset\n"
542"#    ---------- ----------\n",
543	    disk,
544	    sl->sl_text,
545	    sl->sl_nsectors * sl->sl_ntracks,
546	    sl->sl_nsectors * sl->sl_ntracks * sl->sl_ncylinders);
547	for (i = 0; i < 8; i++) {
548		if (sl->sl_part[i].sdkp_nsectors == 0)
549			continue;
550		fprintf(out, "  %c: %10u %10u\n",
551		    'a' + i,
552		    sl->sl_part[i].sdkp_nsectors,
553		    sl->sl_part[i].sdkp_cyloffset);
554	}
555}
556
557static void
558usage(void)
559{
560
561	fprintf(stderr, "usage:"
562"\t%s [-r] disk\n"
563"\t\t(to read label)\n"
564"\t%s -B [-b boot1] [-n] disk\n"
565"\t\t(to install boot program only)\n"
566"\t%s -R [-B [-b boot1]] [-r] [-n] disk protofile\n"
567"\t\t(to restore label)\n"
568"\t%s -e [-B [-b boot1]] [-r] [-n] disk\n"
569"\t\t(to edit label)\n"
570"\t%s -w [-B [-b boot1]] [-r] [-n] disk type\n"
571"\t\t(to write default label)\n",
572	     __progname,
573	     __progname,
574	     __progname,
575	     __progname,
576	     __progname);
577	exit(1);
578}
579