geom_part.c revision 188330
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 188330 2009-02-08 20:19:19Z marcel $");
29
30#include <sys/stat.h>
31
32#include <assert.h>
33#include <err.h>
34#include <errno.h>
35#include <fcntl.h>
36#include <libgeom.h>
37#include <libutil.h>
38#include <paths.h>
39#include <stdint.h>
40#include <stdio.h>
41#include <stdlib.h>
42#include <string.h>
43#include <strings.h>
44#include <unistd.h>
45
46#include "core/geom.h"
47#include "misc/subr.h"
48
49#ifdef STATIC_GEOM_CLASSES
50#define	PUBSYM(x)	gpart_##x
51#else
52#define	PUBSYM(x)	x
53#endif
54
55uint32_t PUBSYM(lib_version) = G_LIB_VERSION;
56uint32_t PUBSYM(version) = 0;
57
58static char optional[] = "";
59static char flags[] = "C";
60
61static char bootcode_param[] = "bootcode";
62static char index_param[] = "index";
63static char partcode_param[] = "partcode";
64
65static void gpart_bootcode(struct gctl_req *, unsigned int);
66static void gpart_issue(struct gctl_req *, unsigned int);
67static void gpart_show(struct gctl_req *, unsigned int);
68
69struct g_command PUBSYM(class_commands)[] = {
70	{ "add", 0, gpart_issue, {
71		{ 'b', "start", NULL, G_TYPE_STRING },
72		{ 's', "size", NULL, G_TYPE_STRING },
73		{ 't', "type", NULL, G_TYPE_STRING },
74		{ 'i', index_param, optional, G_TYPE_STRING },
75		{ 'l', "label", optional, G_TYPE_STRING },
76		{ 'f', "flags", flags, G_TYPE_STRING },
77		G_OPT_SENTINEL },
78	  "geom", NULL
79	},
80	{ "bootcode", 0, gpart_bootcode, {
81		{ 'b', bootcode_param, optional, G_TYPE_STRING },
82		{ 'p', partcode_param, optional, G_TYPE_STRING },
83		{ 'i', index_param, optional, G_TYPE_STRING },
84		{ 'f', "flags", flags, G_TYPE_STRING },
85		G_OPT_SENTINEL },
86	  "geom", NULL
87	},
88	{ "commit", 0, gpart_issue, G_NULL_OPTS, "geom", NULL },
89	{ "create", 0, gpart_issue, {
90		{ 's', "scheme", NULL, G_TYPE_STRING },
91		{ 'n', "entries", optional, G_TYPE_STRING },
92		{ 'f', "flags", flags, G_TYPE_STRING },
93		G_OPT_SENTINEL },
94	  "provider", NULL
95	},
96	{ "delete", 0, gpart_issue, {
97		{ 'i', index_param, NULL, G_TYPE_STRING },
98		{ 'f', "flags", flags, G_TYPE_STRING },
99		G_OPT_SENTINEL },
100	  "geom", NULL
101	},
102	{ "destroy", 0, gpart_issue, {
103		{ 'f', "flags", flags, G_TYPE_STRING },
104		G_OPT_SENTINEL },
105	  "geom", NULL },
106	{ "modify", 0, gpart_issue, {
107		{ 'i', index_param, NULL, G_TYPE_STRING },
108		{ 'l', "label", optional, G_TYPE_STRING },
109		{ 't', "type", optional, G_TYPE_STRING },
110		{ 'f', "flags", flags, G_TYPE_STRING },
111		G_OPT_SENTINEL },
112	  "geom", NULL
113	},
114	{ "set", 0, gpart_issue, {
115		{ 'a', "attrib", NULL, G_TYPE_STRING },
116		{ 'i', index_param, NULL, G_TYPE_STRING },
117		{ 'f', "flags", flags, G_TYPE_STRING },
118		G_OPT_SENTINEL },
119	  "geom", NULL
120	},
121	{ "show", 0, gpart_show, {
122		{ 'l', "show_label", NULL, G_TYPE_BOOL },
123		{ 'r', "show_rawtype", NULL, G_TYPE_BOOL },
124		G_OPT_SENTINEL },
125	  NULL, "[-lr] [geom ...]"
126	},
127	{ "undo", 0, gpart_issue, G_NULL_OPTS, "geom", NULL },
128	{ "unset", 0, gpart_issue, {
129		{ 'a', "attrib", NULL, G_TYPE_STRING },
130		{ 'i', index_param, NULL, G_TYPE_STRING },
131		{ 'f', "flags", flags, G_TYPE_STRING },
132		G_OPT_SENTINEL },
133	  "geom", NULL
134        },
135	G_CMD_SENTINEL
136};
137
138static struct gclass *
139find_class(struct gmesh *mesh, const char *name)
140{
141	struct gclass *classp;
142
143	LIST_FOREACH(classp, &mesh->lg_class, lg_class) {
144		if (strcmp(classp->lg_name, name) == 0)
145			return (classp);
146	}
147	return (NULL);
148}
149
150static struct ggeom *
151find_geom(struct gclass *classp, const char *name)
152{
153	struct ggeom *gp;
154
155	LIST_FOREACH(gp, &classp->lg_geom, lg_geom) {
156		if (strcmp(gp->lg_name, name) == 0)
157			return (gp);
158	}
159	return (NULL);
160}
161
162static const char *
163find_geomcfg(struct ggeom *gp, const char *cfg)
164{
165	struct gconfig *gc;
166
167	LIST_FOREACH(gc, &gp->lg_config, lg_config) {
168		if (!strcmp(gc->lg_name, cfg))
169			return (gc->lg_val);
170	}
171	return (NULL);
172}
173
174static const char *
175find_provcfg(struct gprovider *pp, const char *cfg)
176{
177	struct gconfig *gc;
178
179	LIST_FOREACH(gc, &pp->lg_config, lg_config) {
180		if (!strcmp(gc->lg_name, cfg))
181			return (gc->lg_val);
182	}
183	return (NULL);
184}
185
186static struct gprovider *
187find_provider(struct ggeom *gp, unsigned long long minsector)
188{
189	struct gprovider *pp, *bestpp;
190	const char *s;
191	unsigned long long sector, bestsector;
192
193	bestpp = NULL;
194	LIST_FOREACH(pp, &gp->lg_provider, lg_provider) {
195		s = find_provcfg(pp, "start");
196		if (s == NULL) {
197			s = find_provcfg(pp, "offset");
198			sector = atoll(s) / pp->lg_sectorsize;
199		} else
200			sector = atoll(s);
201
202		if (sector < minsector)
203			continue;
204		if (bestpp != NULL && sector >= bestsector)
205			continue;
206
207		bestpp = pp;
208		bestsector = sector;
209	}
210	return (bestpp);
211}
212
213static const char *
214fmtsize(int64_t rawsz)
215{
216	static char buf[5];
217
218	humanize_number(buf, sizeof(buf), rawsz, "", HN_AUTOSCALE,
219	    HN_B | HN_NOSPACE | HN_DECIMAL);
220	return (buf);
221}
222
223static const char *
224fmtattrib(struct gprovider *pp)
225{
226	static char buf[128];
227	struct gconfig *gc;
228	u_int idx;
229
230	buf[0] = '\0';
231	idx = 0;
232	LIST_FOREACH(gc, &pp->lg_config, lg_config) {
233		if (strcmp(gc->lg_name, "attrib") != 0)
234			continue;
235		idx += snprintf(buf + idx, sizeof(buf) - idx, "%s%s",
236		    (idx == 0) ? " [" : ",", gc->lg_val);
237	}
238	if (idx > 0)
239		snprintf(buf + idx, sizeof(buf) - idx, "] ");
240	return (buf);
241}
242
243static void
244gpart_show_geom(struct ggeom *gp, const char *element)
245{
246	struct gprovider *pp;
247	const char *s, *scheme;
248	unsigned long long first, last, sector, end;
249	unsigned long long length, secsz;
250	int idx, wblocks, wname;
251
252	scheme = find_geomcfg(gp, "scheme");
253	s = find_geomcfg(gp, "first");
254	first = atoll(s);
255	s = find_geomcfg(gp, "last");
256	last = atoll(s);
257	wblocks = strlen(s);
258	wname = strlen(gp->lg_name);
259	pp = LIST_FIRST(&gp->lg_consumer)->lg_provider;
260	secsz = pp->lg_sectorsize;
261	printf("=>%*llu  %*llu  %*s  %s  (%s)\n",
262	    wblocks, first, wblocks, (last - first + 1),
263	    wname, gp->lg_name,
264	    scheme, fmtsize(pp->lg_mediasize));
265
266	while ((pp = find_provider(gp, first)) != NULL) {
267		s = find_provcfg(pp, "start");
268		if (s == NULL) {
269			s = find_provcfg(pp, "offset");
270			sector = atoll(s) / secsz;
271		} else
272			sector = atoll(s);
273
274		s = find_provcfg(pp, "end");
275		if (s == NULL) {
276			s = find_provcfg(pp, "length");
277			length = atoll(s) / secsz;
278			end = sector + length - 1;
279		} else {
280			end = atoll(s);
281			length = end - sector + 1;
282		}
283		s = find_provcfg(pp, "index");
284		idx = atoi(s);
285		if (first < sector) {
286			printf("  %*llu  %*llu  %*s  - free -  (%s)\n",
287			    wblocks, first, wblocks, sector - first,
288			    wname, "",
289			    fmtsize((sector - first) * secsz));
290		}
291		printf("  %*llu  %*llu  %*d  %s %s (%s)\n",
292		    wblocks, sector, wblocks, length,
293		    wname, idx, find_provcfg(pp, element),
294		    fmtattrib(pp), fmtsize(pp->lg_mediasize));
295		first = end + 1;
296	}
297	if (first <= last) {
298		length = last - first + 1;
299		printf("  %*llu  %*llu  %*s  - free -  (%s)\n",
300		    wblocks, first, wblocks, length,
301		    wname, "",
302		    fmtsize(length * secsz));
303	}
304	printf("\n");
305}
306
307static int
308gpart_show_hasopt(struct gctl_req *req, const char *opt, const char *elt)
309{
310
311	if (!gctl_get_int(req, opt))
312		return (0);
313
314	if (elt != NULL)
315		errx(EXIT_FAILURE, "-l and -r are mutually exclusive");
316
317	return (1);
318}
319
320static void
321gpart_show(struct gctl_req *req, unsigned int fl __unused)
322{
323	struct gmesh mesh;
324	struct gclass *classp;
325	struct ggeom *gp;
326	const char *element, *name;
327	int error, i, nargs;
328
329	element = NULL;
330	if (gpart_show_hasopt(req, "show_label", element))
331		element = "label";
332	if (gpart_show_hasopt(req, "show_rawtype", element))
333		element = "rawtype";
334	if (element == NULL)
335		element = "type";
336
337	name = gctl_get_ascii(req, "class");
338	if (name == NULL)
339		abort();
340	error = geom_gettree(&mesh);
341	if (error != 0)
342		errc(EXIT_FAILURE, error, "Cannot get GEOM tree");
343	classp = find_class(&mesh, name);
344	if (classp == NULL) {
345		geom_deletetree(&mesh);
346		errx(EXIT_FAILURE, "Class %s not found.", name);
347	}
348	nargs = gctl_get_int(req, "nargs");
349	if (nargs > 0) {
350		for (i = 0; i < nargs; i++) {
351			name = gctl_get_ascii(req, "arg%d", i);
352			gp = find_geom(classp, name);
353			if (gp != NULL)
354				gpart_show_geom(gp, element);
355			else
356				errx(EXIT_FAILURE, "No such geom: %s.", name);
357		}
358	} else {
359		LIST_FOREACH(gp, &classp->lg_geom, lg_geom) {
360			gpart_show_geom(gp, element);
361		}
362	}
363	geom_deletetree(&mesh);
364}
365
366static void *
367gpart_bootfile_read(const char *bootfile, ssize_t *size)
368{
369	struct stat sb;
370	void *code;
371	int fd;
372
373	if (stat(bootfile, &sb) == -1)
374		err(EXIT_FAILURE, "%s", bootfile);
375	if (!S_ISREG(sb.st_mode))
376		errx(EXIT_FAILURE, "%s: not a regular file", bootfile);
377	if (sb.st_size == 0)
378		errx(EXIT_FAILURE, "%s: empty file", bootfile);
379	if (*size > 0 && sb.st_size >= *size)
380		errx(EXIT_FAILURE, "%s: file too big (%zu limit)", bootfile,
381		    *size);
382
383	*size = sb.st_size;
384
385	fd = open(bootfile, O_RDONLY);
386	if (fd == -1)
387		err(EXIT_FAILURE, "%s", bootfile);
388	code = malloc(*size);
389	if (code == NULL)
390		err(EXIT_FAILURE, NULL);
391	if (read(fd, code, *size) != *size)
392		err(EXIT_FAILURE, "%s", bootfile);
393	close(fd);
394
395	return (code);
396}
397
398static void
399gpart_write_partcode(struct gctl_req *req, int idx, void *code, ssize_t size)
400{
401	char dsf[128];
402	struct gmesh mesh;
403	struct gclass *classp;
404	struct ggeom *gp;
405	struct gprovider *pp;
406	const char *s;
407	char *buf;
408	off_t bsize;
409	int error, fd;
410
411	s = gctl_get_ascii(req, "class");
412	if (s == NULL)
413		abort();
414	error = geom_gettree(&mesh);
415	if (error != 0)
416		errc(EXIT_FAILURE, error, "Cannot get GEOM tree");
417	classp = find_class(&mesh, s);
418	if (classp == NULL) {
419		geom_deletetree(&mesh);
420		errx(EXIT_FAILURE, "Class %s not found.", s);
421	}
422	s = gctl_get_ascii(req, "geom");
423	gp = find_geom(classp, s);
424	if (gp == NULL)
425		errx(EXIT_FAILURE, "No such geom: %s.", s);
426
427	LIST_FOREACH(pp, &gp->lg_provider, lg_provider) {
428		s = find_provcfg(pp, "index");
429		if (s == NULL)
430			continue;
431		if (atoi(s) == idx)
432			break;
433	}
434
435	if (pp != NULL) {
436		snprintf(dsf, sizeof(dsf), "/dev/%s", pp->lg_name);
437		fd = open(dsf, O_WRONLY);
438		if (fd == -1)
439			err(EXIT_FAILURE, "%s", dsf);
440		if (lseek(fd, size, SEEK_SET) != size)
441			errx(EXIT_FAILURE, "%s: not enough space", dsf);
442		if (lseek(fd, 0, SEEK_SET) != 0)
443			err(EXIT_FAILURE, "%s", dsf);
444
445		/*
446		 * When writing to a disk device, the write must be
447		 * sector aligned and not write to any partial sectors,
448		 * so round up the buffer size to the next sector and zero it.
449		 */
450		bsize = (size + pp->lg_sectorsize - 1) /
451		    pp->lg_sectorsize * pp->lg_sectorsize;
452		buf = calloc(1, bsize);
453		if (buf == NULL)
454			err(EXIT_FAILURE, "%s", dsf);
455		bcopy(code, buf, size);
456		if (write(fd, buf, bsize) != bsize)
457			err(EXIT_FAILURE, "%s", dsf);
458		free(buf);
459		close(fd);
460	} else
461		errx(EXIT_FAILURE, "invalid partition index");
462
463	geom_deletetree(&mesh);
464}
465
466static void
467gpart_bootcode(struct gctl_req *req, unsigned int fl)
468{
469	const char *s;
470	char *sp;
471	void *bootcode, *partcode;
472	size_t bootsize, partsize;
473	int error, idx;
474
475	if (gctl_has_param(req, bootcode_param)) {
476		s = gctl_get_ascii(req, bootcode_param);
477		bootsize = 64 * 1024;		/* Arbitrary limit. */
478		bootcode = gpart_bootfile_read(s, &bootsize);
479		error = gctl_change_param(req, bootcode_param, bootsize,
480		    bootcode);
481		if (error)
482			errc(EXIT_FAILURE, error, "internal error");
483	} else {
484		bootcode = NULL;
485		bootsize = 0;
486	}
487
488	if (gctl_has_param(req, partcode_param)) {
489		s = gctl_get_ascii(req, partcode_param);
490		partsize = bootsize * 1024;
491		partcode = gpart_bootfile_read(s, &partsize);
492		error = gctl_delete_param(req, partcode_param);
493		if (error)
494			errc(EXIT_FAILURE, error, "internal error");
495	} else {
496		partcode = NULL;
497		partsize = 0;
498	}
499
500	if (gctl_has_param(req, index_param)) {
501		if (partcode == NULL)
502			errx(EXIT_FAILURE, "-i is only valid with -p");
503		s = gctl_get_ascii(req, index_param);
504		idx = strtol(s, &sp, 10);
505		if (idx < 1 || *s == '\0' || *sp != '\0')
506			errx(EXIT_FAILURE, "invalid partition index");
507		error = gctl_delete_param(req, index_param);
508		if (error)
509			errc(EXIT_FAILURE, error, "internal error");
510	} else
511		idx = 0;
512
513	if (partcode != NULL) {
514		if (idx == 0)
515			errx(EXIT_FAILURE, "missing -i option");
516		gpart_write_partcode(req, idx, partcode, partsize);
517	} else {
518		if (bootcode == NULL)
519			errx(EXIT_FAILURE, "no -b nor -p");
520	}
521
522	if (bootcode != NULL)
523		gpart_issue(req, fl);
524}
525
526static void
527gpart_issue(struct gctl_req *req, unsigned int fl __unused)
528{
529	char buf[4096];
530	char *errmsg;
531	const char *errstr;
532	int error, status;
533
534	bzero(buf, sizeof(buf));
535	gctl_rw_param(req, "output", sizeof(buf), buf);
536	errstr = gctl_issue(req);
537	if (errstr == NULL || errstr[0] == '\0') {
538		if (buf[0] != '\0')
539			printf("%s", buf);
540		status = EXIT_SUCCESS;
541		goto done;
542	}
543
544	error = strtol(errstr, &errmsg, 0);
545	if (errmsg != errstr) {
546		while (errmsg[0] == ' ')
547			errmsg++;
548		if (errmsg[0] != '\0')
549			warnc(error, "%s", errmsg);
550		else
551			warnc(error, NULL);
552	} else
553		warnx("%s", errmsg);
554
555	status = EXIT_FAILURE;
556
557 done:
558	gctl_free(req);
559	exit(status);
560}
561