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