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