geom_part.c revision 208777
1/*-
2 * Copyright (c) 2007, 2008 Marcel Moolenaar
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 AUTHORS 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 AUTHORS 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#include <sys/cdefs.h>
28__FBSDID("$FreeBSD: head/sbin/geom/class/part/geom_part.c 208777 2010-06-03 13:01:55Z marius $");
29
30#include <sys/stat.h>
31#include <sys/vtoc.h>
32
33#include <assert.h>
34#include <err.h>
35#include <errno.h>
36#include <fcntl.h>
37#include <libgeom.h>
38#include <libutil.h>
39#include <paths.h>
40#include <stdint.h>
41#include <stdio.h>
42#include <stdlib.h>
43#include <string.h>
44#include <strings.h>
45#include <unistd.h>
46
47#include "core/geom.h"
48#include "misc/subr.h"
49
50#ifdef STATIC_GEOM_CLASSES
51#define	PUBSYM(x)	gpart_##x
52#else
53#define	PUBSYM(x)	x
54#endif
55
56uint32_t PUBSYM(lib_version) = G_LIB_VERSION;
57uint32_t PUBSYM(version) = 0;
58
59static char autofill[] = "*";
60static char optional[] = "";
61static char flags[] = "C";
62
63static const char const bootcode_param[] = "bootcode";
64static const char const index_param[] = "index";
65static const char const partcode_param[] = "partcode";
66
67static struct gclass *find_class(struct gmesh *, const char *);
68static struct ggeom * find_geom(struct gclass *, const char *);
69static const char *find_geomcfg(struct ggeom *, const char *);
70static const char *find_provcfg(struct gprovider *, const char *);
71static struct gprovider *find_provider(struct ggeom *,
72    unsigned long long);
73static const char *fmtsize(int64_t);
74static int gpart_autofill(struct gctl_req *);
75static int gpart_autofill_resize(struct gctl_req *);
76static void gpart_bootcode(struct gctl_req *, unsigned int);
77static void *gpart_bootfile_read(const char *, ssize_t *);
78static void gpart_issue(struct gctl_req *, unsigned int);
79static void gpart_show(struct gctl_req *, unsigned int);
80static void gpart_show_geom(struct ggeom *, const char *);
81static int gpart_show_hasopt(struct gctl_req *, const char *, const char *);
82static void gpart_write_partcode(struct ggeom *, int, void *, ssize_t);
83static void gpart_write_partcode_vtoc8(struct ggeom *, int, void *);
84
85struct g_command PUBSYM(class_commands)[] = {
86	{ "add", 0, gpart_issue, {
87		{ 'b', "start", autofill, G_TYPE_ASCLBA },
88		{ 's', "size", autofill, G_TYPE_ASCLBA },
89		{ 't', "type", NULL, G_TYPE_STRING },
90		{ 'i', index_param, optional, G_TYPE_ASCNUM },
91		{ 'l', "label", optional, G_TYPE_STRING },
92		{ 'f', "flags", flags, G_TYPE_STRING },
93		G_OPT_SENTINEL },
94	  "geom", NULL
95	},
96	{ "bootcode", 0, gpart_bootcode, {
97		{ 'b', bootcode_param, optional, G_TYPE_STRING },
98		{ 'p', partcode_param, optional, G_TYPE_STRING },
99		{ 'i', index_param, optional, G_TYPE_ASCNUM },
100		{ 'f', "flags", flags, G_TYPE_STRING },
101		G_OPT_SENTINEL },
102	  "geom", NULL
103	},
104	{ "commit", 0, gpart_issue, G_NULL_OPTS, "geom", NULL },
105	{ "create", 0, gpart_issue, {
106		{ 's', "scheme", NULL, G_TYPE_STRING },
107		{ 'n', "entries", optional, G_TYPE_ASCNUM },
108		{ 'f', "flags", flags, G_TYPE_STRING },
109		G_OPT_SENTINEL },
110	  "provider", NULL
111	},
112	{ "delete", 0, gpart_issue, {
113		{ 'i', index_param, NULL, G_TYPE_ASCNUM },
114		{ 'f', "flags", flags, G_TYPE_STRING },
115		G_OPT_SENTINEL },
116	  "geom", NULL
117	},
118	{ "destroy", 0, gpart_issue, {
119		{ 'f', "flags", flags, G_TYPE_STRING },
120		G_OPT_SENTINEL },
121	  "geom", NULL },
122	{ "modify", 0, gpart_issue, {
123		{ 'i', index_param, NULL, G_TYPE_ASCNUM },
124		{ 'l', "label", optional, G_TYPE_STRING },
125		{ 't', "type", optional, G_TYPE_STRING },
126		{ 'f', "flags", flags, G_TYPE_STRING },
127		G_OPT_SENTINEL },
128	  "geom", NULL
129	},
130	{ "set", 0, gpart_issue, {
131		{ 'a', "attrib", NULL, G_TYPE_STRING },
132		{ 'i', index_param, NULL, G_TYPE_ASCNUM },
133		{ 'f', "flags", flags, G_TYPE_STRING },
134		G_OPT_SENTINEL },
135	  "geom", NULL
136	},
137	{ "show", 0, gpart_show, {
138		{ 'l', "show_label", NULL, G_TYPE_BOOL },
139		{ 'r', "show_rawtype", NULL, G_TYPE_BOOL },
140		G_OPT_SENTINEL },
141	  NULL, "[-lr] [geom ...]"
142	},
143	{ "undo", 0, gpart_issue, G_NULL_OPTS, "geom", NULL },
144	{ "unset", 0, gpart_issue, {
145		{ 'a', "attrib", NULL, G_TYPE_STRING },
146		{ 'i', index_param, NULL, G_TYPE_ASCNUM },
147		{ 'f', "flags", flags, G_TYPE_STRING },
148		G_OPT_SENTINEL },
149	  "geom", NULL
150	},
151	{ "resize", 0, gpart_issue, {
152		{ 's', "size", autofill, G_TYPE_ASCLBA },
153		{ 'i', index_param, NULL, G_TYPE_ASCNUM },
154		{ 'f', "flags", flags, G_TYPE_STRING },
155		G_OPT_SENTINEL },
156	  "geom", NULL
157	},
158	G_CMD_SENTINEL
159};
160
161static struct gclass *
162find_class(struct gmesh *mesh, const char *name)
163{
164	struct gclass *classp;
165
166	LIST_FOREACH(classp, &mesh->lg_class, lg_class) {
167		if (strcmp(classp->lg_name, name) == 0)
168			return (classp);
169	}
170	return (NULL);
171}
172
173static struct ggeom *
174find_geom(struct gclass *classp, const char *name)
175{
176	struct ggeom *gp;
177
178	LIST_FOREACH(gp, &classp->lg_geom, lg_geom) {
179		if (strcmp(gp->lg_name, name) == 0)
180			return (gp);
181	}
182	return (NULL);
183}
184
185static const char *
186find_geomcfg(struct ggeom *gp, const char *cfg)
187{
188	struct gconfig *gc;
189
190	LIST_FOREACH(gc, &gp->lg_config, lg_config) {
191		if (!strcmp(gc->lg_name, cfg))
192			return (gc->lg_val);
193	}
194	return (NULL);
195}
196
197static const char *
198find_provcfg(struct gprovider *pp, const char *cfg)
199{
200	struct gconfig *gc;
201
202	LIST_FOREACH(gc, &pp->lg_config, lg_config) {
203		if (!strcmp(gc->lg_name, cfg))
204			return (gc->lg_val);
205	}
206	return (NULL);
207}
208
209static struct gprovider *
210find_provider(struct ggeom *gp, unsigned long long minsector)
211{
212	struct gprovider *pp, *bestpp;
213	const char *s;
214	unsigned long long sector, bestsector;
215
216	bestpp = NULL;
217	bestsector = 0;
218	LIST_FOREACH(pp, &gp->lg_provider, lg_provider) {
219		s = find_provcfg(pp, "start");
220		if (s == NULL) {
221			s = find_provcfg(pp, "offset");
222			sector = atoll(s) / pp->lg_sectorsize;
223		} else
224			sector = atoll(s);
225
226		if (sector < minsector)
227			continue;
228		if (bestpp != NULL && sector >= bestsector)
229			continue;
230
231		bestpp = pp;
232		bestsector = sector;
233	}
234	return (bestpp);
235}
236
237static const char *
238fmtsize(int64_t rawsz)
239{
240	static char buf[5];
241
242	humanize_number(buf, sizeof(buf), rawsz, "", HN_AUTOSCALE,
243	    HN_B | HN_NOSPACE | HN_DECIMAL);
244	return (buf);
245}
246
247static const char *
248fmtattrib(struct gprovider *pp)
249{
250	static char buf[128];
251	struct gconfig *gc;
252	u_int idx;
253
254	buf[0] = '\0';
255	idx = 0;
256	LIST_FOREACH(gc, &pp->lg_config, lg_config) {
257		if (strcmp(gc->lg_name, "attrib") != 0)
258			continue;
259		idx += snprintf(buf + idx, sizeof(buf) - idx, "%s%s",
260		    (idx == 0) ? " [" : ",", gc->lg_val);
261	}
262	if (idx > 0)
263		snprintf(buf + idx, sizeof(buf) - idx, "] ");
264	return (buf);
265}
266
267static int
268gpart_autofill_resize(struct gctl_req *req)
269{
270	struct gmesh mesh;
271	struct gclass *cp;
272	struct ggeom *gp;
273	struct gprovider *pp;
274	unsigned long long last, size, start, new_size;
275	unsigned long long lba, new_lba;
276	const char *s;
277	char *val;
278	int error, idx;
279
280	s = gctl_get_ascii(req, "size");
281	if (*s == '*')
282		new_size = (unsigned long long)atoll(s);
283	else
284		return (0);
285
286	s = gctl_get_ascii(req, index_param);
287	idx = strtol(s, &val, 10);
288	if (idx < 1 || *s == '\0' || *val != '\0')
289		errx(EXIT_FAILURE, "invalid partition index");
290
291	error = geom_gettree(&mesh);
292	if (error)
293		return (error);
294	s = gctl_get_ascii(req, "class");
295	if (s == NULL)
296		abort();
297	cp = find_class(&mesh, s);
298	if (cp == NULL)
299		errx(EXIT_FAILURE, "Class %s not found.", s);
300	s = gctl_get_ascii(req, "geom");
301	if (s == NULL)
302		abort();
303	gp = find_geom(cp, s);
304	if (gp == NULL)
305		errx(EXIT_FAILURE, "No such geom: %s.", s);
306	last = atoll(find_geomcfg(gp, "last"));
307
308	LIST_FOREACH(pp, &gp->lg_provider, lg_provider) {
309		s = find_provcfg(pp, "index");
310		if (s == NULL)
311			continue;
312		if (atoi(s) == idx)
313			break;
314	}
315	if (pp == NULL)
316		errx(EXIT_FAILURE, "invalid partition index");
317
318	s = find_provcfg(pp, "start");
319	if (s == NULL) {
320		s = find_provcfg(pp, "offset");
321		start = atoll(s) / pp->lg_sectorsize;
322	} else
323		start = atoll(s);
324	s = find_provcfg(pp, "end");
325	if (s == NULL) {
326		s = find_provcfg(pp, "length");
327		lba = start + atoll(s) / pp->lg_sectorsize;
328	} else
329		lba = atoll(s) + 1;
330
331	if (lba > last)
332		return (ENOSPC);
333	size = lba - start;
334	pp = find_provider(gp, lba);
335	if (pp == NULL)
336		new_size = last - start + 1;
337	else {
338		s = find_provcfg(pp, "start");
339		if (s == NULL) {
340			s = find_provcfg(pp, "offset");
341			new_lba = atoll(s) / pp->lg_sectorsize;
342		} else
343			new_lba = atoll(s);
344		/* Is there any free space between current and
345		 * next providers?
346		 */
347		if (new_lba > lba)
348			new_size = new_lba - start;
349		else
350			return (ENOSPC);
351	}
352	asprintf(&val, "%llu", new_size);
353	if (val == NULL)
354		return (ENOMEM);
355	gctl_change_param(req, "size", -1, val);
356
357	return (0);
358}
359
360static int
361gpart_autofill(struct gctl_req *req)
362{
363	struct gmesh mesh;
364	struct gclass *cp;
365	struct ggeom *gp;
366	struct gprovider *pp;
367	unsigned long long first, last;
368	unsigned long long size, start;
369	unsigned long long lba, len, grade;
370	const char *s;
371	char *val;
372	int error, has_size, has_start;
373
374	s = gctl_get_ascii(req, "verb");
375	if (strcmp(s, "resize") == 0)
376		return gpart_autofill_resize(req);
377	if (strcmp(s, "add") != 0)
378		return (0);
379
380	s = gctl_get_ascii(req, "size");
381	has_size = (*s == '*') ? 0 : 1;
382	size = (has_size) ? (unsigned long long)atoll(s) : 0ULL;
383
384	s = gctl_get_ascii(req, "start");
385	has_start = (*s == '*') ? 0 : 1;
386	start = (has_start) ? (unsigned long long)atoll(s) : ~0ULL;
387
388	/* No autofill necessary. */
389	if (has_size && has_start)
390		return (0);
391
392	error = geom_gettree(&mesh);
393	if (error)
394		return (error);
395	s = gctl_get_ascii(req, "class");
396	if (s == NULL)
397		abort();
398	cp = find_class(&mesh, s);
399	if (cp == NULL)
400		errx(EXIT_FAILURE, "Class %s not found.", s);
401	s = gctl_get_ascii(req, "geom");
402	if (s == NULL)
403		abort();
404	gp = find_geom(cp, s);
405	if (gp == NULL)
406		errx(EXIT_FAILURE, "No such geom: %s.", s);
407	first = atoll(find_geomcfg(gp, "first"));
408	last = atoll(find_geomcfg(gp, "last"));
409	grade = ~0ULL;
410	while ((pp = find_provider(gp, first)) != NULL) {
411		s = find_provcfg(pp, "start");
412		if (s == NULL) {
413			s = find_provcfg(pp, "offset");
414			lba = atoll(s) / pp->lg_sectorsize;
415		} else
416			lba = atoll(s);
417
418		if (first < lba) {
419			/* Free space [first, lba> */
420			len = lba - first;
421			if (has_size) {
422				if (len >= size && len - size < grade) {
423					start = first;
424					grade = len - size;
425				}
426			} else if (has_start) {
427				if (start >= first && start < lba) {
428					size = lba - start;
429					grade = start - first;
430				}
431			} else {
432				if (grade == ~0ULL || len > size) {
433					start = first;
434					size = len;
435					grade = 0;
436				}
437			}
438		}
439
440		s = find_provcfg(pp, "end");
441		if (s == NULL) {
442			s = find_provcfg(pp, "length");
443			first = lba + atoll(s) / pp->lg_sectorsize;
444		} else
445			first = atoll(s) + 1;
446	}
447	if (first <= last) {
448		/* Free space [first-last] */
449		len = last - first + 1;
450		if (has_size) {
451			if (len >= size && len - size < grade) {
452				start = first;
453				grade = len - size;
454			}
455		} else if (has_start) {
456			if (start >= first && start <= last) {
457				size = last - start + 1;
458				grade = start - first;
459			}
460		} else {
461			if (grade == ~0ULL || len > size) {
462				start = first;
463				size = len;
464				grade = 0;
465			}
466		}
467	}
468
469	if (grade == ~0ULL)
470		return (ENOSPC);
471
472	if (!has_size) {
473		asprintf(&val, "%llu", size);
474		if (val == NULL)
475			return (ENOMEM);
476		gctl_change_param(req, "size", -1, val);
477	}
478	if (!has_start) {
479		asprintf(&val, "%llu", start);
480		if (val == NULL)
481			return (ENOMEM);
482		gctl_change_param(req, "start", -1, val);
483	}
484	return (0);
485}
486
487static void
488gpart_show_geom(struct ggeom *gp, const char *element)
489{
490	struct gprovider *pp;
491	const char *s, *scheme;
492	unsigned long long first, last, sector, end;
493	unsigned long long length, secsz;
494	int idx, wblocks, wname;
495
496	scheme = find_geomcfg(gp, "scheme");
497	s = find_geomcfg(gp, "first");
498	first = atoll(s);
499	s = find_geomcfg(gp, "last");
500	last = atoll(s);
501	wblocks = strlen(s);
502	wname = strlen(gp->lg_name);
503	pp = LIST_FIRST(&gp->lg_consumer)->lg_provider;
504	secsz = pp->lg_sectorsize;
505	printf("=>%*llu  %*llu  %*s  %s  (%s)\n",
506	    wblocks, first, wblocks, (last - first + 1),
507	    wname, gp->lg_name,
508	    scheme, fmtsize(pp->lg_mediasize));
509
510	while ((pp = find_provider(gp, first)) != NULL) {
511		s = find_provcfg(pp, "start");
512		if (s == NULL) {
513			s = find_provcfg(pp, "offset");
514			sector = atoll(s) / secsz;
515		} else
516			sector = atoll(s);
517
518		s = find_provcfg(pp, "end");
519		if (s == NULL) {
520			s = find_provcfg(pp, "length");
521			length = atoll(s) / secsz;
522			end = sector + length - 1;
523		} else {
524			end = atoll(s);
525			length = end - sector + 1;
526		}
527		s = find_provcfg(pp, "index");
528		idx = atoi(s);
529		if (first < sector) {
530			printf("  %*llu  %*llu  %*s  - free -  (%s)\n",
531			    wblocks, first, wblocks, sector - first,
532			    wname, "",
533			    fmtsize((sector - first) * secsz));
534		}
535		printf("  %*llu  %*llu  %*d  %s %s (%s)\n",
536		    wblocks, sector, wblocks, length,
537		    wname, idx, find_provcfg(pp, element),
538		    fmtattrib(pp), fmtsize(pp->lg_mediasize));
539		first = end + 1;
540	}
541	if (first <= last) {
542		length = last - first + 1;
543		printf("  %*llu  %*llu  %*s  - free -  (%s)\n",
544		    wblocks, first, wblocks, length,
545		    wname, "",
546		    fmtsize(length * secsz));
547	}
548	printf("\n");
549}
550
551static int
552gpart_show_hasopt(struct gctl_req *req, const char *opt, const char *elt)
553{
554
555	if (!gctl_get_int(req, opt))
556		return (0);
557
558	if (elt != NULL)
559		errx(EXIT_FAILURE, "-l and -r are mutually exclusive");
560
561	return (1);
562}
563
564static void
565gpart_show(struct gctl_req *req, unsigned int fl __unused)
566{
567	struct gmesh mesh;
568	struct gclass *classp;
569	struct ggeom *gp;
570	const char *element, *name;
571	int error, i, nargs;
572
573	element = NULL;
574	if (gpart_show_hasopt(req, "show_label", element))
575		element = "label";
576	if (gpart_show_hasopt(req, "show_rawtype", element))
577		element = "rawtype";
578	if (element == NULL)
579		element = "type";
580
581	name = gctl_get_ascii(req, "class");
582	if (name == NULL)
583		abort();
584	error = geom_gettree(&mesh);
585	if (error != 0)
586		errc(EXIT_FAILURE, error, "Cannot get GEOM tree");
587	classp = find_class(&mesh, name);
588	if (classp == NULL) {
589		geom_deletetree(&mesh);
590		errx(EXIT_FAILURE, "Class %s not found.", name);
591	}
592	nargs = gctl_get_int(req, "nargs");
593	if (nargs > 0) {
594		for (i = 0; i < nargs; i++) {
595			name = gctl_get_ascii(req, "arg%d", i);
596			gp = find_geom(classp, name);
597			if (gp != NULL)
598				gpart_show_geom(gp, element);
599			else
600				errx(EXIT_FAILURE, "No such geom: %s.", name);
601		}
602	} else {
603		LIST_FOREACH(gp, &classp->lg_geom, lg_geom) {
604			gpart_show_geom(gp, element);
605		}
606	}
607	geom_deletetree(&mesh);
608}
609
610static void *
611gpart_bootfile_read(const char *bootfile, ssize_t *size)
612{
613	struct stat sb;
614	void *code;
615	int fd;
616
617	if (stat(bootfile, &sb) == -1)
618		err(EXIT_FAILURE, "%s", bootfile);
619	if (!S_ISREG(sb.st_mode))
620		errx(EXIT_FAILURE, "%s: not a regular file", bootfile);
621	if (sb.st_size == 0)
622		errx(EXIT_FAILURE, "%s: empty file", bootfile);
623	if (*size > 0 && sb.st_size > *size)
624		errx(EXIT_FAILURE, "%s: file too big (%zu limit)", bootfile,
625		    *size);
626
627	*size = sb.st_size;
628
629	fd = open(bootfile, O_RDONLY);
630	if (fd == -1)
631		err(EXIT_FAILURE, "%s", bootfile);
632	code = malloc(*size);
633	if (code == NULL)
634		err(EXIT_FAILURE, NULL);
635	if (read(fd, code, *size) != *size)
636		err(EXIT_FAILURE, "%s", bootfile);
637	close(fd);
638
639	return (code);
640}
641
642static void
643gpart_write_partcode(struct ggeom *gp, int idx, void *code, ssize_t size)
644{
645	char dsf[128];
646	struct gprovider *pp;
647	const char *s;
648	char *buf;
649	off_t bsize;
650	int fd;
651
652	LIST_FOREACH(pp, &gp->lg_provider, lg_provider) {
653		s = find_provcfg(pp, "index");
654		if (s == NULL)
655			continue;
656		if (atoi(s) == idx)
657			break;
658	}
659
660	if (pp != NULL) {
661		snprintf(dsf, sizeof(dsf), "/dev/%s", pp->lg_name);
662		fd = open(dsf, O_WRONLY);
663		if (fd == -1)
664			err(EXIT_FAILURE, "%s", dsf);
665		if (lseek(fd, size, SEEK_SET) != size)
666			errx(EXIT_FAILURE, "%s: not enough space", dsf);
667		if (lseek(fd, 0, SEEK_SET) != 0)
668			err(EXIT_FAILURE, "%s", dsf);
669
670		/*
671		 * When writing to a disk device, the write must be
672		 * sector aligned and not write to any partial sectors,
673		 * so round up the buffer size to the next sector and zero it.
674		 */
675		bsize = (size + pp->lg_sectorsize - 1) /
676		    pp->lg_sectorsize * pp->lg_sectorsize;
677		buf = calloc(1, bsize);
678		if (buf == NULL)
679			err(EXIT_FAILURE, "%s", dsf);
680		bcopy(code, buf, size);
681		if (write(fd, buf, bsize) != bsize)
682			err(EXIT_FAILURE, "%s", dsf);
683		free(buf);
684		close(fd);
685	} else
686		errx(EXIT_FAILURE, "invalid partition index");
687}
688
689static void
690gpart_write_partcode_vtoc8(struct ggeom *gp, int idx, void *code)
691{
692	char dsf[128];
693	struct gprovider *pp;
694	const char *s;
695	int installed, fd;
696
697	installed = 0;
698	LIST_FOREACH(pp, &gp->lg_provider, lg_provider) {
699		s = find_provcfg(pp, "index");
700		if (s == NULL)
701			continue;
702		if (idx != 0 && atoi(s) != idx)
703			continue;
704		snprintf(dsf, sizeof(dsf), "/dev/%s", pp->lg_name);
705		if (pp->lg_sectorsize != sizeof(struct vtoc8))
706			errx(EXIT_FAILURE, "%s: unexpected sector "
707			    "size (%d)\n", dsf, pp->lg_sectorsize);
708		fd = open(dsf, O_WRONLY);
709		if (fd == -1)
710			err(EXIT_FAILURE, "%s", dsf);
711		if (lseek(fd, VTOC_BOOTSIZE, SEEK_SET) != VTOC_BOOTSIZE)
712			continue;
713		/*
714		 * We ignore the first VTOC_BOOTSIZE bytes of boot code in
715		 * order to avoid overwriting the label.
716		 */
717		if (lseek(fd, sizeof(struct vtoc8), SEEK_SET) !=
718		    sizeof(struct vtoc8))
719			err(EXIT_FAILURE, "%s", dsf);
720		if (write(fd, (caddr_t)code + sizeof(struct vtoc8),
721		    VTOC_BOOTSIZE - sizeof(struct vtoc8)) != VTOC_BOOTSIZE -
722		    sizeof(struct vtoc8))
723			err(EXIT_FAILURE, "%s", dsf);
724		installed++;
725		close(fd);
726		if (idx != 0 && atoi(s) == idx)
727			break;
728	}
729	if (installed == 0)
730		errx(EXIT_FAILURE, "%s: no partitions", gp->lg_name);
731}
732
733static void
734gpart_bootcode(struct gctl_req *req, unsigned int fl)
735{
736	struct gmesh mesh;
737	struct gclass *classp;
738	struct ggeom *gp;
739	const char *s;
740	char *sp;
741	void *bootcode, *partcode;
742	size_t bootsize, partsize;
743	int error, idx, vtoc8;
744
745	if (gctl_has_param(req, bootcode_param)) {
746		s = gctl_get_ascii(req, bootcode_param);
747		bootsize = 800 * 1024;		/* Arbitrary limit. */
748		bootcode = gpart_bootfile_read(s, &bootsize);
749		error = gctl_change_param(req, bootcode_param, bootsize,
750		    bootcode);
751		if (error)
752			errc(EXIT_FAILURE, error, "internal error");
753	} else {
754		bootcode = NULL;
755		bootsize = 0;
756	}
757
758	s = gctl_get_ascii(req, "class");
759	if (s == NULL)
760		abort();
761	error = geom_gettree(&mesh);
762	if (error != 0)
763		errc(EXIT_FAILURE, error, "Cannot get GEOM tree");
764	classp = find_class(&mesh, s);
765	if (classp == NULL) {
766		geom_deletetree(&mesh);
767		errx(EXIT_FAILURE, "Class %s not found.", s);
768	}
769	s = gctl_get_ascii(req, "geom");
770	if (s == NULL)
771		abort();
772	gp = find_geom(classp, s);
773	if (gp == NULL)
774		errx(EXIT_FAILURE, "No such geom: %s.", s);
775	s = find_geomcfg(gp, "scheme");
776	vtoc8 = 0;
777	if (strcmp(s, "VTOC8") == 0)
778		vtoc8 = 1;
779
780	if (gctl_has_param(req, partcode_param)) {
781		s = gctl_get_ascii(req, partcode_param);
782		partsize = vtoc8 != 0 ? VTOC_BOOTSIZE : bootsize * 1024;
783		partcode = gpart_bootfile_read(s, &partsize);
784		error = gctl_delete_param(req, partcode_param);
785		if (error)
786			errc(EXIT_FAILURE, error, "internal error");
787	} else {
788		partcode = NULL;
789		partsize = 0;
790	}
791
792	if (gctl_has_param(req, index_param)) {
793		if (partcode == NULL)
794			errx(EXIT_FAILURE, "-i is only valid with -p");
795		s = gctl_get_ascii(req, index_param);
796		idx = strtol(s, &sp, 10);
797		if (idx < 1 || *s == '\0' || *sp != '\0')
798			errx(EXIT_FAILURE, "invalid partition index");
799		error = gctl_delete_param(req, index_param);
800		if (error)
801			errc(EXIT_FAILURE, error, "internal error");
802	} else
803		idx = 0;
804
805	if (partcode != NULL) {
806		if (vtoc8 == 0) {
807			if (idx == 0)
808				errx(EXIT_FAILURE, "missing -i option");
809			gpart_write_partcode(gp, idx, partcode, partsize);
810		} else
811			gpart_write_partcode_vtoc8(gp, idx, partcode);
812	} else
813		if (bootcode == NULL)
814			errx(EXIT_FAILURE, "no -b nor -p");
815
816	if (bootcode != NULL)
817		gpart_issue(req, fl);
818
819	geom_deletetree(&mesh);
820}
821
822static void
823gpart_issue(struct gctl_req *req, unsigned int fl __unused)
824{
825	char buf[4096];
826	char *errmsg;
827	const char *errstr;
828	int error, status;
829
830	/* autofill parameters (if applicable). */
831	error = gpart_autofill(req);
832	if (error) {
833		warnc(error, "autofill");
834		status = EXIT_FAILURE;
835		goto done;
836	}
837
838	bzero(buf, sizeof(buf));
839	gctl_rw_param(req, "output", sizeof(buf), buf);
840	errstr = gctl_issue(req);
841	if (errstr == NULL || errstr[0] == '\0') {
842		if (buf[0] != '\0')
843			printf("%s", buf);
844		status = EXIT_SUCCESS;
845		goto done;
846	}
847
848	error = strtol(errstr, &errmsg, 0);
849	if (errmsg != errstr) {
850		while (errmsg[0] == ' ')
851			errmsg++;
852		if (errmsg[0] != '\0')
853			warnc(error, "%s", errmsg);
854		else
855			warnc(error, NULL);
856	} else
857		warnx("%s", errmsg);
858
859	status = EXIT_FAILURE;
860
861 done:
862	gctl_free(req);
863	exit(status);
864}
865