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