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