geom_ctl.c revision 140367
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
36#include <sys/cdefs.h>
37__FBSDID("$FreeBSD: head/sys/geom/geom_ctl.c 140367 2005-01-17 07:14:24Z phk $");
38
39#include "opt_geom.h"
40
41#include <sys/param.h>
42#include <sys/systm.h>
43#include <sys/kernel.h>
44#include <sys/sysctl.h>
45#include <sys/bio.h>
46#include <sys/conf.h>
47#include <sys/disk.h>
48#include <sys/malloc.h>
49#include <sys/sysctl.h>
50#include <sys/sbuf.h>
51
52#include <sys/lock.h>
53#include <sys/mutex.h>
54
55#include <vm/vm.h>
56#include <vm/vm_extern.h>
57
58#include <geom/geom.h>
59#include <geom/geom_int.h>
60#define GCTL_TABLE 1
61#include <geom/geom_ctl.h>
62
63#include <machine/stdarg.h>
64
65static d_ioctl_t g_ctl_ioctl;
66
67static struct cdevsw g_ctl_cdevsw = {
68	.d_version =	D_VERSION,
69	.d_flags =	D_NEEDGIANT,
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	va_list ap;
96
97	if (req == NULL)
98		return (EINVAL);
99
100	/* We only record the first error */
101	if (req->nerror)
102		return (req->nerror);
103
104	va_start(ap, fmt);
105	sbuf_vprintf(req->serror, fmt, ap);
106	va_end(ap);
107	sbuf_finish(req->serror);
108	if (g_debugflags & G_F_CTLDUMP)
109		printf("gctl %p error \"%s\"\n", req, sbuf_data(req->serror));
110	req->nerror = copyout(sbuf_data(req->serror), req->error,
111	    imin(req->lerror, sbuf_len(req->serror) + 1));
112	if (!req->nerror)
113		req->nerror = EINVAL;
114	return (req->nerror);
115}
116
117/*
118 * Allocate space and copyin() something.
119 * XXX: this should really be a standard function in the kernel.
120 */
121static void *
122geom_alloc_copyin(struct gctl_req *req, void *uaddr, size_t len)
123{
124	void *ptr;
125
126	ptr = g_malloc(len, M_WAITOK);
127	if (ptr == NULL)
128		req->nerror = ENOMEM;
129	else
130		req->nerror = copyin(uaddr, ptr, len);
131	if (!req->nerror)
132		return (ptr);
133	if (ptr != NULL)
134		g_free(ptr);
135	return (NULL);
136}
137
138static void
139gctl_copyin(struct gctl_req *req)
140{
141	int error, i;
142	struct gctl_req_arg *ap;
143	char *p;
144
145	ap = geom_alloc_copyin(req, req->arg, req->narg * sizeof(*ap));
146	if (ap == NULL) {
147		req->nerror = ENOMEM;
148		req->arg = NULL;
149		return;
150	}
151
152	/* Nothing have been copyin()'ed yet */
153	for (i = 0; i < req->narg; i++) {
154		ap[i].flag &= ~(GCTL_PARAM_NAMEKERNEL|GCTL_PARAM_VALUEKERNEL);
155		ap[i].flag &= ~GCTL_PARAM_CHANGED;
156		ap[i].kvalue = NULL;
157	}
158
159	error = 0;
160	for (i = 0; i < req->narg; i++) {
161		if (ap[i].nlen < 1 || ap[i].nlen > SPECNAMELEN) {
162			error = gctl_error(req,
163			    "wrong param name length %d: %d", i, ap[i].nlen);
164			break;
165		}
166		p = geom_alloc_copyin(req, ap[i].name, ap[i].nlen);
167		if (p == NULL)
168			break;
169		if (p[ap[i].nlen - 1] != '\0') {
170			error = gctl_error(req, "unterminated param name");
171			g_free(p);
172			break;
173		}
174		ap[i].name = p;
175		ap[i].flag |= GCTL_PARAM_NAMEKERNEL;
176		if (ap[i].len <= 0) {
177			error = gctl_error(req, "negative param length");
178			break;
179		}
180		p = geom_alloc_copyin(req, ap[i].value, ap[i].len);
181		if (p == NULL)
182			break;
183		if ((ap[i].flag & GCTL_PARAM_ASCII) &&
184		    p[ap[i].len - 1] != '\0') {
185			error = gctl_error(req, "unterminated param value");
186			g_free(p);
187			break;
188		}
189		ap[i].kvalue = p;
190		ap[i].flag |= GCTL_PARAM_VALUEKERNEL;
191	}
192	req->arg = ap;
193	return;
194}
195
196static void
197gctl_copyout(struct gctl_req *req)
198{
199	int error, i;
200	struct gctl_req_arg *ap;
201
202	if (req->nerror)
203		return;
204	error = 0;
205	ap = req->arg;
206	for (i = 0; i < req->narg; i++, ap++) {
207		if (!(ap->flag & GCTL_PARAM_CHANGED))
208			continue;
209		error = copyout(ap->kvalue, ap->value, ap->len);
210		if (!error)
211			continue;
212		req->nerror = error;
213		return;
214	}
215	return;
216}
217
218static void
219gctl_free(struct gctl_req *req)
220{
221	int i;
222
223	if (req->arg == NULL)
224		return;
225	for (i = 0; i < req->narg; i++) {
226		if (req->arg[i].flag & GCTL_PARAM_NAMEKERNEL)
227			g_free(req->arg[i].name);
228		if ((req->arg[i].flag & GCTL_PARAM_VALUEKERNEL) &&
229		    req->arg[i].len > 0)
230			g_free(req->arg[i].kvalue);
231	}
232	g_free(req->arg);
233	sbuf_delete(req->serror);
234}
235
236static void
237gctl_dump(struct gctl_req *req)
238{
239	u_int i;
240	int j;
241	struct gctl_req_arg *ap;
242
243	printf("Dump of gctl request at %p:\n", req);
244	if (req->nerror > 0) {
245		printf("  nerror:\t%d\n", req->nerror);
246		if (sbuf_len(req->serror) > 0)
247			printf("  error:\t\"%s\"\n", sbuf_data(req->serror));
248	}
249	for (i = 0; i < req->narg; i++) {
250		ap = &req->arg[i];
251		if (!(ap->flag & GCTL_PARAM_NAMEKERNEL))
252			printf("  param:\t%d@%p", ap->nlen, ap->name);
253		else
254			printf("  param:\t\"%s\"", ap->name);
255		printf(" [%s%s%d] = ",
256		    ap->flag & GCTL_PARAM_RD ? "R" : "",
257		    ap->flag & GCTL_PARAM_WR ? "W" : "",
258		    ap->len);
259		if (!(ap->flag & GCTL_PARAM_VALUEKERNEL)) {
260			printf(" =@ %p", ap->value);
261		} else if (ap->flag & GCTL_PARAM_ASCII) {
262			printf("\"%s\"", (char *)ap->kvalue);
263		} else if (ap->len > 0) {
264			for (j = 0; j < ap->len && j < 512; j++)
265				printf(" %02x", ((u_char *)ap->kvalue)[j]);
266		} else {
267			printf(" = %p", ap->kvalue);
268		}
269		printf("\n");
270	}
271}
272
273void
274gctl_set_param(struct gctl_req *req, const char *param, void const *ptr, int len)
275{
276	int i;
277	struct gctl_req_arg *ap;
278
279	for (i = 0; i < req->narg; i++) {
280		ap = &req->arg[i];
281		if (strcmp(param, ap->name))
282			continue;
283		if (!(ap->flag & GCTL_PARAM_WR)) {
284			gctl_error(req, "No write access %s argument", param);
285			return;
286		}
287		if (ap->len < len) {
288			gctl_error(req, "Wrong length %s argument", param);
289			return;
290		}
291		bcopy(ptr, ap->kvalue, len);
292		ap->flag |= GCTL_PARAM_CHANGED;
293		return;
294	}
295	gctl_error(req, "Missing %s argument", param);
296	return;
297}
298
299void *
300gctl_get_param(struct gctl_req *req, const char *param, int *len)
301{
302	int i;
303	void *p;
304	struct gctl_req_arg *ap;
305
306	for (i = 0; i < req->narg; i++) {
307		ap = &req->arg[i];
308		if (strcmp(param, ap->name))
309			continue;
310		if (!(ap->flag & GCTL_PARAM_RD))
311			continue;
312		p = ap->kvalue;
313		if (len != NULL)
314			*len = ap->len;
315		return (p);
316	}
317	return (NULL);
318}
319
320char const *
321gctl_get_asciiparam(struct gctl_req *req, const char *param)
322{
323	int i;
324	char const *p;
325	struct gctl_req_arg *ap;
326
327	for (i = 0; i < req->narg; i++) {
328		ap = &req->arg[i];
329		if (strcmp(param, ap->name))
330			continue;
331		if (!(ap->flag & GCTL_PARAM_RD))
332			continue;
333		p = ap->kvalue;
334		if (ap->len < 1) {
335			gctl_error(req, "No length argument (%s)", param);
336			return (NULL);
337		}
338		if (p[ap->len - 1] != '\0') {
339			gctl_error(req, "Unterminated argument (%s)", param);
340			return (NULL);
341		}
342		return (p);
343	}
344	return (NULL);
345}
346
347void *
348gctl_get_paraml(struct gctl_req *req, const char *param, int len)
349{
350	int i;
351	void *p;
352
353	p = gctl_get_param(req, param, &i);
354	if (p == NULL)
355		gctl_error(req, "Missing %s argument", param);
356	else if (i != len) {
357		p = NULL;
358		gctl_error(req, "Wrong length %s argument", param);
359	}
360	return (p);
361}
362
363struct g_class *
364gctl_get_class(struct gctl_req *req, char const *arg)
365{
366	char const *p;
367	struct g_class *cp;
368
369	p = gctl_get_asciiparam(req, arg);
370	if (p == NULL)
371		return (NULL);
372	LIST_FOREACH(cp, &g_classes, class) {
373		if (!strcmp(p, cp->name))
374			return (cp);
375	}
376	gctl_error(req, "Class not found");
377	return (NULL);
378}
379
380struct g_geom *
381gctl_get_geom(struct gctl_req *req, struct g_class *mpr, char const *arg)
382{
383	char const *p;
384	struct g_class *mp;
385	struct g_geom *gp;
386
387	p = gctl_get_asciiparam(req, arg);
388	if (p != NULL) {
389		LIST_FOREACH(mp, &g_classes, class) {
390			if (mpr != NULL && mpr != mp)
391				continue;
392			LIST_FOREACH(gp, &mp->geom, geom) {
393				if (!strcmp(p, gp->name))
394					return (gp);
395			}
396		}
397	}
398	gctl_error(req, "Geom not found");
399	return (NULL);
400}
401
402struct g_provider *
403gctl_get_provider(struct gctl_req *req, char const *arg)
404{
405	char const *p;
406	struct g_provider *pp;
407
408	p = gctl_get_asciiparam(req, arg);
409	if (p == NULL)
410		return (NULL);
411	pp = g_provider_by_name(p);
412	if (pp != NULL)
413		return (pp);
414	gctl_error(req, "Provider not found");
415	return (NULL);
416}
417
418static void
419g_ctl_req(void *arg, int flag __unused)
420{
421	struct g_class *mp;
422	struct gctl_req *req;
423	char const *verb;
424
425	g_topology_assert();
426	req = arg;
427	mp = gctl_get_class(req, "class");
428	if (mp == NULL) {
429		gctl_error(req, "Class not found");
430		return;
431	}
432	verb = gctl_get_param(req, "verb", NULL);
433	if (mp->ctlreq == NULL)
434		gctl_error(req, "Class takes no requests");
435	else
436		mp->ctlreq(req, mp, verb);
437	g_topology_assert();
438}
439
440
441static int
442g_ctl_ioctl_ctl(struct cdev *dev, u_long cmd, caddr_t data, int fflag, struct thread *td)
443{
444	struct gctl_req *req;
445	int nerror;
446
447	req = (void *)data;
448	req->nerror = 0;
449	req->serror = sbuf_new(NULL, NULL, 0, SBUF_AUTOEXTEND);
450	/* It is an error if we cannot return an error text */
451	if (req->lerror < 2)
452		return (EINVAL);
453	if (!useracc(req->error, req->lerror, VM_PROT_WRITE))
454		return (EINVAL);
455
456	/* Check the version */
457	if (req->version != GCTL_VERSION)
458		return (gctl_error(req,
459		    "kernel and libgeom version mismatch."));
460
461	/* Get things on board */
462	gctl_copyin(req);
463
464	if (g_debugflags & G_F_CTLDUMP)
465		gctl_dump(req);
466
467	if (!req->nerror) {
468		g_waitfor_event(g_ctl_req, req, M_WAITOK, NULL);
469		gctl_copyout(req);
470	}
471
472	nerror = req->nerror;
473	gctl_free(req);
474	return (nerror);
475}
476
477static int
478g_ctl_ioctl(struct cdev *dev, u_long cmd, caddr_t data, int fflag, struct thread *td)
479{
480	int error;
481
482	switch(cmd) {
483	case GEOM_CTL:
484		error = g_ctl_ioctl_ctl(dev, cmd, data, fflag, td);
485		break;
486	default:
487		error = ENOIOCTL;
488		break;
489	}
490	return (error);
491
492}
493