geom_ctl.c revision 113892
1/*-
2 * Copyright (c) 2002 Poul-Henning Kamp
3 * Copyright (c) 2002 Networks Associates Technology, Inc.
4 * All rights reserved.
5 *
6 * This software was developed for the FreeBSD Project by Poul-Henning Kamp
7 * and NAI Labs, the Security Research Division of Network Associates, Inc.
8 * under DARPA/SPAWAR contract N66001-01-C-8035 ("CBOSS"), as part of the
9 * DARPA CHATS research program.
10 *
11 * Redistribution and use in source and binary forms, with or without
12 * modification, are permitted provided that the following conditions
13 * are met:
14 * 1. Redistributions of source code must retain the above copyright
15 *    notice, this list of conditions and the following disclaimer.
16 * 2. Redistributions in binary form must reproduce the above copyright
17 *    notice, this list of conditions and the following disclaimer in the
18 *    documentation and/or other materials provided with the distribution.
19 * 3. The names of the authors may not be used to endorse or promote
20 *    products derived from this software without specific prior written
21 *    permission.
22 *
23 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
24 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
25 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
26 * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
27 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
28 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
29 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
30 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
31 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
32 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
33 * SUCH DAMAGE.
34 *
35 * $FreeBSD: head/sys/geom/geom_ctl.c 113892 2003-04-23 07:50:01Z phk $
36 */
37
38#include "opt_geom.h"
39
40#include <sys/param.h>
41#include <sys/systm.h>
42#include <sys/kernel.h>
43#include <sys/sysctl.h>
44#include <sys/bio.h>
45#include <sys/conf.h>
46#include <sys/disk.h>
47#include <sys/malloc.h>
48#include <sys/sysctl.h>
49#include <sys/sbuf.h>
50
51#include <sys/lock.h>
52#include <sys/mutex.h>
53
54#include <vm/vm.h>
55#include <vm/vm_extern.h>
56
57#include <geom/geom.h>
58#include <geom/geom_int.h>
59#define GCTL_TABLE 1
60#include <geom/geom_ctl.h>
61#include <geom/geom_ext.h>
62
63#include <machine/stdarg.h>
64
65static d_ioctl_t g_ctl_ioctl;
66
67static struct cdevsw g_ctl_cdevsw = {
68	.d_open =	nullopen,
69	.d_close =	nullclose,
70	.d_ioctl =	g_ctl_ioctl,
71	.d_name =	"g_ctl",
72};
73
74void
75g_ctl_init(void)
76{
77
78	make_dev(&g_ctl_cdevsw, 0,
79	    UID_ROOT, GID_OPERATOR, 0640, PATH_GEOM_CTL);
80	KASSERT(GCTL_PARAM_RD == VM_PROT_READ,
81		("GCTL_PARAM_RD != VM_PROT_READ"));
82	KASSERT(GCTL_PARAM_WR == VM_PROT_WRITE,
83		("GCTL_PARAM_WR != VM_PROT_WRITE"));
84}
85
86/*
87 * Report an error back to the user in ascii format.  Return whatever copyout
88 * returned, or EINVAL if it succeeded.
89 * XXX: should not be static.
90 * XXX: should take printf like args.
91 */
92int
93gctl_error(struct gctl_req *req, const char *fmt, ...)
94{
95	int error;
96	va_list ap;
97	struct sbuf *sb;
98
99	sb = sbuf_new(NULL, NULL, 0, SBUF_AUTOEXTEND);
100	if (sb == NULL) {
101		error = copyout(fmt, req->error,
102			    imin(req->lerror, strlen(fmt) + 1));
103	} else {
104		va_start(ap, fmt);
105		sbuf_vprintf(sb, fmt, ap);
106		sbuf_finish(sb);
107		if (g_debugflags & G_F_CTLDUMP)
108			printf("gctl %p error \"%s\"\n", req, sbuf_data(sb));
109		error = copyout(sbuf_data(sb), req->error,
110		    imin(req->lerror, sbuf_len(sb) + 1));
111	}
112	if (!error)
113		error = EINVAL;
114	sbuf_delete(sb);
115	return (error);
116}
117
118/*
119 * Allocate space and copyin() something.
120 * XXX: this should really be a standard function in the kernel.
121 */
122static void *
123geom_alloc_copyin(struct gctl_req *req, void *uaddr, size_t len, int *errp)
124{
125	int error;
126	void *ptr;
127
128	ptr = g_malloc(len, M_WAITOK);
129	if (ptr == NULL)
130		error = ENOMEM;
131	else
132		error = copyin(uaddr, ptr, len);
133	if (!error)
134		return (ptr);
135	gctl_error(req, "no access to argument");
136	*errp = error;
137	if (ptr != NULL)
138		g_free(ptr);
139	return (NULL);
140}
141
142
143/*
144 * XXX: This function is a nightmare.  It walks through the request and
145 * XXX: makes sure that the various bits and pieces are there and copies
146 * XXX: some of them into kernel memory to make things easier.
147 * XXX: I really wish we had a standard marshalling layer somewhere.
148 */
149
150static int
151gctl_copyin(struct gctl_req *req)
152{
153	int error, i, j;
154	struct gctl_req_arg *ap;
155	char *p;
156
157	error = 0;
158	ap = geom_alloc_copyin(req, req->arg, req->narg * sizeof(*ap), &error);
159	if (ap == NULL) {
160		gctl_error(req, "copyin() of arguments failed");
161		return (error);
162	}
163
164	for (i = 0; !error && i < req->narg; i++) {
165		if (ap[i].len > 0 &&
166		    !useracc(ap[i].value, ap[i].len,
167		    ap[i].flag & GCTL_PARAM_RW))
168			error = gctl_error(req, "no access to param data");
169		if (error)
170			break;
171		p = NULL;
172		if (ap[i].nlen < 1 || ap[i].nlen > SPECNAMELEN) {
173			error = gctl_error(req, "wrong param name length");
174			break;
175		}
176		p = geom_alloc_copyin(req, ap[i].name, ap[i].nlen, &error);
177		if (p == NULL)
178			break;
179		if (p[ap[i].nlen - 1] != '\0') {
180			error = gctl_error(req, "unterminated param name");
181			g_free(p);
182			break;
183		}
184		ap[i].name = p;
185		ap[i].nlen = 0;
186	}
187	if (!error) {
188		req->arg = ap;
189		return (0);
190	}
191	for (j = 0; j < i; j++)
192		if (ap[j].nlen == 0 && ap[j].name != NULL)
193			g_free(ap[j].name);
194	g_free(ap);
195	return (error);
196}
197
198static void
199gctl_dump(struct gctl_req *req)
200{
201	u_int i;
202	int j, error;
203	struct gctl_req_arg *ap;
204	void *p;
205
206
207	printf("Dump of gctl %s request at %p:\n", req->reqt->name, req);
208	if (req->lerror > 0) {
209		p = geom_alloc_copyin(req, req->error, req->lerror, &error);
210		if (p != NULL) {
211			((char *)p)[req->lerror - 1] = '\0';
212			printf("  error:\t\"%s\"\n", (char *)p);
213			g_free(p);
214		}
215	}
216	for (i = 0; i < req->narg; i++) {
217		ap = &req->arg[i];
218		printf("  param:\t\"%s\"", ap->name);
219		printf(" [%s%s%d] = ",
220		    ap->flag & GCTL_PARAM_RD ? "R" : "",
221		    ap->flag & GCTL_PARAM_WR ? "W" : "",
222		    ap->len);
223		if (ap->flag & GCTL_PARAM_ASCII) {
224			p = geom_alloc_copyin(req, ap->value, ap->len, &error);
225			if (p != NULL) {
226				((char *)p)[ap->len - 1] = '\0';
227				printf("\"%s\"", (char *)p);
228			}
229			g_free(p);
230		} else if (ap->len > 0) {
231			p = geom_alloc_copyin(req, ap->value, ap->len, &error);
232			for (j = 0; j < ap->len; j++)
233				printf(" %02x", ((u_char *)p)[j]);
234			g_free(p);
235		} else {
236			printf(" = %p", ap->value);
237		}
238		printf("\n");
239	}
240}
241
242void *
243gctl_get_param(struct gctl_req *req, const char *param, int *len)
244{
245	int i, error, j;
246	void *p;
247	struct gctl_req_arg *ap;
248
249	for (i = 0; i < req->narg; i++) {
250		ap = &req->arg[i];
251		if (strcmp(param, ap->name))
252			continue;
253		if (!(ap->flag & GCTL_PARAM_RD))
254			continue;
255		if (ap->len > 0)
256			j = ap->len;
257		else
258			j = 0;
259		if (j != 0)
260			p = geom_alloc_copyin(req, ap->value, j, &error);
261			/* XXX: should not fail, tested prviously */
262		else
263			p = ap->value;
264		if (len != NULL)
265			*len = j;
266		return (p);
267	}
268	return (NULL);
269}
270
271static struct g_class*
272gctl_get_class(struct gctl_req *req)
273{
274	char *p;
275	int len;
276	struct g_class *cp;
277
278	p = gctl_get_param(req, "class", &len);
279	if (p == NULL)
280		return (NULL);
281	if (p[len - 1] != '\0') {
282		gctl_error(req, "Unterminated class name");
283		g_free(p);
284		return (NULL);
285	}
286	LIST_FOREACH(cp, &g_classes, class) {
287		if (!strcmp(p, cp->name)) {
288			g_free(p);
289			return (cp);
290		}
291	}
292	gctl_error(req, "Class not found");
293	return (NULL);
294}
295
296static struct g_geom*
297gctl_get_geom(struct gctl_req *req, struct g_class *mpr)
298{
299	char *p;
300	int len;
301	struct g_class *mp;
302	struct g_geom *gp;
303
304	p = gctl_get_param(req, "geom", &len);
305	if (p == NULL)
306		return (NULL);
307	if (p[len - 1] != '\0') {
308		gctl_error(req, "Unterminated provider name");
309		g_free(p);
310		return (NULL);
311	}
312	LIST_FOREACH(mp, &g_classes, class) {
313		if (mpr != NULL && mpr != mp)
314			continue;
315		LIST_FOREACH(gp, &mp->geom, geom) {
316			if (!strcmp(p, gp->name)) {
317				g_free(p);
318				return (gp);
319			}
320		}
321	}
322	gctl_error(req, "Geom not found");
323	return (NULL);
324}
325
326static struct g_provider*
327gctl_get_provider(struct gctl_req *req)
328{
329	char *p;
330	int len;
331	struct g_class *cp;
332	struct g_geom *gp;
333	struct g_provider *pp;
334
335	p = gctl_get_param(req, "provider", &len);
336	if (p == NULL)
337		return (NULL);
338	if (p[len - 1] != '\0') {
339		gctl_error(req, "Unterminated provider name");
340		g_free(p);
341		return (NULL);
342	}
343	LIST_FOREACH(cp, &g_classes, class) {
344		LIST_FOREACH(gp, &cp->geom, geom) {
345			LIST_FOREACH(pp, &gp->provider, provider) {
346				if (!strcmp(p, pp->name)) {
347					g_free(p);
348					return (pp);
349				}
350			}
351		}
352	}
353	gctl_error(req, "Provider not found");
354	return (NULL);
355}
356
357static int
358gctl_create_geom(struct gctl_req *req)
359{
360	struct g_class *mp;
361	struct g_provider *pp;
362	int error;
363
364	g_topology_assert();
365	mp = gctl_get_class(req);
366	if (mp == NULL)
367		return (gctl_error(req, "Class not found"));
368	if (mp->create_geom == NULL)
369		return (gctl_error(req, "Class has no create_geom method"));
370	pp = gctl_get_provider(req);
371	error = mp->create_geom(req, mp, pp);
372	g_topology_assert();
373	return (error);
374}
375
376static int
377gctl_destroy_geom(struct gctl_req *req)
378{
379	struct g_class *mp;
380	struct g_geom *gp;
381	int error;
382
383	g_topology_assert();
384	mp = gctl_get_class(req);
385	if (mp == NULL)
386		return (gctl_error(req, "Class not found"));
387	if (mp->destroy_geom == NULL)
388		return (gctl_error(req, "Class has no destroy_geom method"));
389	gp = gctl_get_geom(req, mp);
390	if (gp == NULL)
391		return (gctl_error(req, "Geom not specified"));
392	if (gp->class != mp)
393		return (gctl_error(req, "Geom not of specificed class"));
394	error = mp->destroy_geom(req, mp, gp);
395	g_topology_assert();
396	return (error);
397}
398
399static int
400gctl_config_geom(struct gctl_req *req)
401{
402	struct g_class *mp;
403	struct g_geom *gp;
404	char *verb;
405	int error, vlen;
406
407	g_topology_assert();
408	mp = gctl_get_class(req);
409	if (mp == NULL)
410		return (gctl_error(req, "Class not found"));
411	if (mp->config_geom == NULL)
412		return (gctl_error(req, "Class has no config_geom method"));
413	gp = gctl_get_geom(req, mp);
414	if (gp == NULL)
415		return (gctl_error(req, "Geom not specified"));
416	if (gp->class != mp)
417		return (gctl_error(req, "Geom not of specificed class"));
418	verb = gctl_get_param(req, "verb", &vlen);
419	if (verb == NULL)
420		return (gctl_error(req, "Missing verb parameter"));
421	if (vlen < 2) {
422		g_free(verb);
423		return (gctl_error(req, "Too short verb parameter"));
424	}
425	if (verb[vlen - 1] != '\0') {
426		g_free(verb);
427		return (gctl_error(req, "Unterminated verb parameter"));
428	}
429	error = mp->config_geom(req, gp, verb);
430	g_free(verb);
431	g_topology_assert();
432	return (error);
433}
434
435/*
436 * Handle ioctl from libgeom::geom_ctl.c
437 */
438static int
439g_ctl_ioctl_ctl(dev_t dev, u_long cmd, caddr_t data, int fflag, struct thread *td)
440{
441	int error;
442	int i;
443	struct gctl_req *req;
444
445	req = (void *)data;
446	/* It is an error if we cannot return an error text */
447	if (req->lerror < 1)
448		return (EINVAL);
449	if (!useracc(req->error, req->lerror, VM_PROT_WRITE))
450		return (EINVAL);
451
452	/* Check the version */
453	if (req->version != GCTL_VERSION)
454		return (gctl_error(req,
455		    "kernel and libgeom version mismatch."));
456
457	/* Check the request type */
458	for (i = 0; gcrt[i].request != GCTL_INVALID_REQUEST; i++)
459		if (gcrt[i].request == req->request)
460			break;
461	if (gcrt[i].request == GCTL_INVALID_REQUEST)
462		return (gctl_error(req, "invalid request"));
463	req->reqt = &gcrt[i];
464
465	/* Get things on board */
466	error = gctl_copyin(req);
467	if (error)
468		return (error);
469
470	if (g_debugflags & G_F_CTLDUMP)
471		gctl_dump(req);
472	g_topology_lock();
473	switch (req->request) {
474	case GCTL_CREATE_GEOM:
475		error = gctl_create_geom(req);
476		break;
477	case GCTL_DESTROY_GEOM:
478		error = gctl_destroy_geom(req);
479		break;
480	case GCTL_CONFIG_GEOM:
481		error = gctl_config_geom(req);
482		break;
483	default:
484		error = gctl_error(req, "XXX: TBD");
485		break;
486	}
487	g_topology_unlock();
488	return (error);
489}
490
491static int
492g_ctl_ioctl(dev_t dev, u_long cmd, caddr_t data, int fflag, struct thread *td)
493{
494	int error;
495
496	switch(cmd) {
497	case GEOM_CTL:
498		DROP_GIANT();
499		error = g_ctl_ioctl_ctl(dev, cmd, data, fflag, td);
500		PICKUP_GIANT();
501		break;
502	default:
503		error = ENOTTY;
504		break;
505	}
506	return (error);
507
508}
509