geom_ctl.c revision 136839
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 136839 2004-10-23 20:52:15Z 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		if (ap[i].len == 0) {
181			ap[i].kvalue = ap[i].value;
182			ap[i].flag |= GCTL_PARAM_VALUEKERNEL;
183			continue;
184		}
185		p = geom_alloc_copyin(req, ap[i].value, ap[i].len);
186		if (p == NULL)
187			break;
188		if ((ap[i].flag & GCTL_PARAM_ASCII) &&
189		    p[ap[i].len - 1] != '\0') {
190			error = gctl_error(req, "unterminated param value");
191			g_free(p);
192			break;
193		}
194		ap[i].kvalue = p;
195		ap[i].flag |= GCTL_PARAM_VALUEKERNEL;
196	}
197	req->arg = ap;
198	return;
199}
200
201static void
202gctl_copyout(struct gctl_req *req)
203{
204	int error, i;
205	struct gctl_req_arg *ap;
206
207	if (req->nerror)
208		return;
209	error = 0;
210	ap = req->arg;
211	for (i = 0; i < req->narg; i++, ap++) {
212		if (!(ap->flag & GCTL_PARAM_CHANGED))
213			continue;
214		error = copyout(ap->kvalue, ap->value, ap->len);
215		if (!error)
216			continue;
217		req->nerror = error;
218		return;
219	}
220	return;
221}
222
223static void
224gctl_free(struct gctl_req *req)
225{
226	int i;
227
228	if (req->arg == NULL)
229		return;
230	for (i = 0; i < req->narg; i++) {
231		if (req->arg[i].flag & GCTL_PARAM_NAMEKERNEL)
232			g_free(req->arg[i].name);
233		if ((req->arg[i].flag & GCTL_PARAM_VALUEKERNEL) &&
234		    req->arg[i].len > 0)
235			g_free(req->arg[i].kvalue);
236	}
237	g_free(req->arg);
238	sbuf_delete(req->serror);
239}
240
241static void
242gctl_dump(struct gctl_req *req)
243{
244	u_int i;
245	int j;
246	struct gctl_req_arg *ap;
247
248	printf("Dump of gctl request at %p:\n", req);
249	if (req->nerror > 0) {
250		printf("  nerror:\t%d\n", req->nerror);
251		if (sbuf_len(req->serror) > 0)
252			printf("  error:\t\"%s\"\n", sbuf_data(req->serror));
253	}
254	for (i = 0; i < req->narg; i++) {
255		ap = &req->arg[i];
256		if (!(ap->flag & GCTL_PARAM_NAMEKERNEL))
257			printf("  param:\t%d@%p", ap->nlen, ap->name);
258		else
259			printf("  param:\t\"%s\"", ap->name);
260		printf(" [%s%s%d] = ",
261		    ap->flag & GCTL_PARAM_RD ? "R" : "",
262		    ap->flag & GCTL_PARAM_WR ? "W" : "",
263		    ap->len);
264		if (!(ap->flag & GCTL_PARAM_VALUEKERNEL)) {
265			printf(" =@ %p", ap->value);
266		} else if (ap->flag & GCTL_PARAM_ASCII) {
267			printf("\"%s\"", (char *)ap->kvalue);
268		} else if (ap->len > 0) {
269			for (j = 0; j < ap->len && j < 512; j++)
270				printf(" %02x", ((u_char *)ap->kvalue)[j]);
271		} else {
272			printf(" = %p", ap->kvalue);
273		}
274		printf("\n");
275	}
276}
277
278void
279gctl_set_param(struct gctl_req *req, const char *param, void const *ptr, int len)
280{
281	int i;
282	struct gctl_req_arg *ap;
283
284	for (i = 0; i < req->narg; i++) {
285		ap = &req->arg[i];
286		if (strcmp(param, ap->name))
287			continue;
288		if (!(ap->flag & GCTL_PARAM_WR)) {
289			gctl_error(req, "No write access %s argument", param);
290			return;
291		}
292		if (ap->len < len) {
293			gctl_error(req, "Wrong length %s argument", param);
294			return;
295		}
296		bcopy(ptr, ap->kvalue, len);
297		ap->flag |= GCTL_PARAM_CHANGED;
298		return;
299	}
300	gctl_error(req, "Missing %s argument", param);
301	return;
302}
303
304void *
305gctl_get_param(struct gctl_req *req, const char *param, int *len)
306{
307	int i;
308	void *p;
309	struct gctl_req_arg *ap;
310
311	for (i = 0; i < req->narg; i++) {
312		ap = &req->arg[i];
313		if (strcmp(param, ap->name))
314			continue;
315		if (!(ap->flag & GCTL_PARAM_RD))
316			continue;
317		p = ap->kvalue;
318		if (len != NULL)
319			*len = ap->len;
320		return (p);
321	}
322	return (NULL);
323}
324
325char const *
326gctl_get_asciiparam(struct gctl_req *req, const char *param)
327{
328	int i;
329	char const *p;
330	struct gctl_req_arg *ap;
331
332	for (i = 0; i < req->narg; i++) {
333		ap = &req->arg[i];
334		if (strcmp(param, ap->name))
335			continue;
336		if (!(ap->flag & GCTL_PARAM_RD))
337			continue;
338		p = ap->kvalue;
339		if (ap->len < 1) {
340			gctl_error(req, "No length argument (%s)", param);
341			return (NULL);
342		}
343		if (p[ap->len - 1] != '\0') {
344			gctl_error(req, "Unterminated argument (%s)", param);
345			return (NULL);
346		}
347		return (p);
348	}
349	return (NULL);
350}
351
352void *
353gctl_get_paraml(struct gctl_req *req, const char *param, int len)
354{
355	int i;
356	void *p;
357
358	p = gctl_get_param(req, param, &i);
359	if (p == NULL)
360		gctl_error(req, "Missing %s argument", param);
361	else if (i != len) {
362		p = NULL;
363		gctl_error(req, "Wrong length %s argument", param);
364	}
365	return (p);
366}
367
368struct g_class *
369gctl_get_class(struct gctl_req *req, char const *arg)
370{
371	char const *p;
372	struct g_class *cp;
373
374	p = gctl_get_asciiparam(req, arg);
375	if (p == NULL)
376		return (NULL);
377	LIST_FOREACH(cp, &g_classes, class) {
378		if (!strcmp(p, cp->name))
379			return (cp);
380	}
381	gctl_error(req, "Class not found");
382	return (NULL);
383}
384
385struct g_geom *
386gctl_get_geom(struct gctl_req *req, struct g_class *mpr, char const *arg)
387{
388	char const *p;
389	struct g_class *mp;
390	struct g_geom *gp;
391
392	p = gctl_get_asciiparam(req, arg);
393	if (p != NULL) {
394		LIST_FOREACH(mp, &g_classes, class) {
395			if (mpr != NULL && mpr != mp)
396				continue;
397			LIST_FOREACH(gp, &mp->geom, geom) {
398				if (!strcmp(p, gp->name))
399					return (gp);
400			}
401		}
402	}
403	gctl_error(req, "Geom not found");
404	return (NULL);
405}
406
407struct g_provider *
408gctl_get_provider(struct gctl_req *req, char const *arg)
409{
410	char const *p;
411	struct g_provider *pp;
412
413	p = gctl_get_asciiparam(req, arg);
414	if (p == NULL)
415		return (NULL);
416	pp = g_provider_by_name(p);
417	if (pp != NULL)
418		return (pp);
419	gctl_error(req, "Provider not found");
420	return (NULL);
421}
422
423static void
424g_ctl_req(void *arg, int flag __unused)
425{
426	struct g_class *mp;
427	struct gctl_req *req;
428	char const *verb;
429
430	g_topology_assert();
431	req = arg;
432	mp = gctl_get_class(req, "class");
433	if (mp == NULL) {
434		gctl_error(req, "Class not found");
435		return;
436	}
437	verb = gctl_get_param(req, "verb", NULL);
438	if (mp->ctlreq == NULL)
439		gctl_error(req, "Class takes no requests");
440	else
441		mp->ctlreq(req, mp, verb);
442	g_topology_assert();
443}
444
445
446static int
447g_ctl_ioctl_ctl(struct cdev *dev, u_long cmd, caddr_t data, int fflag, struct thread *td)
448{
449	struct gctl_req *req;
450	int nerror;
451
452	req = (void *)data;
453	req->nerror = 0;
454	req->serror = sbuf_new(NULL, NULL, 0, SBUF_AUTOEXTEND);
455	/* It is an error if we cannot return an error text */
456	if (req->lerror < 2)
457		return (EINVAL);
458	if (!useracc(req->error, req->lerror, VM_PROT_WRITE))
459		return (EINVAL);
460
461	/* Check the version */
462	if (req->version != GCTL_VERSION)
463		return (gctl_error(req,
464		    "kernel and libgeom version mismatch."));
465
466	/* Get things on board */
467	gctl_copyin(req);
468
469	if (g_debugflags & G_F_CTLDUMP)
470		gctl_dump(req);
471
472	if (!req->nerror) {
473		g_waitfor_event(g_ctl_req, req, M_WAITOK, NULL);
474		gctl_copyout(req);
475	}
476
477	nerror = req->nerror;
478	gctl_free(req);
479	return (nerror);
480}
481
482static int
483g_ctl_ioctl(struct cdev *dev, u_long cmd, caddr_t data, int fflag, struct thread *td)
484{
485	int error;
486
487	switch(cmd) {
488	case GEOM_CTL:
489		error = g_ctl_ioctl_ctl(dev, cmd, data, fflag, td);
490		break;
491	default:
492		error = ENOIOCTL;
493		break;
494	}
495	return (error);
496
497}
498