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