geom_ctl.c revision 157581
1121934Sharti/*-
2121934Sharti * Copyright (c) 2002 Poul-Henning Kamp
3121934Sharti * Copyright (c) 2002 Networks Associates Technology, Inc.
4121934Sharti * All rights reserved.
5121934Sharti *
6121934Sharti * This software was developed for the FreeBSD Project by Poul-Henning Kamp
7121934Sharti * and NAI Labs, the Security Research Division of Network Associates, Inc.
8121934Sharti * under DARPA/SPAWAR contract N66001-01-C-8035 ("CBOSS"), as part of the
9121934Sharti * DARPA CHATS research program.
10121934Sharti *
11121934Sharti * Redistribution and use in source and binary forms, with or without
12121934Sharti * modification, are permitted provided that the following conditions
13121934Sharti * are met:
14121934Sharti * 1. Redistributions of source code must retain the above copyright
15121934Sharti *    notice, this list of conditions and the following disclaimer.
16121934Sharti * 2. Redistributions in binary form must reproduce the above copyright
17121934Sharti *    notice, this list of conditions and the following disclaimer in the
18121934Sharti *    documentation and/or other materials provided with the distribution.
19121934Sharti * 3. The names of the authors may not be used to endorse or promote
20121934Sharti *    products derived from this software without specific prior written
21121934Sharti *    permission.
22121934Sharti *
23121934Sharti * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
24121934Sharti * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
25121934Sharti * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
26121934Sharti * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
27121934Sharti * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
28121934Sharti * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
29131826Sharti * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
30121934Sharti * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
31121934Sharti * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
32121934Sharti * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
33121934Sharti * SUCH DAMAGE.
34121934Sharti */
35121934Sharti
36121934Sharti#include <sys/cdefs.h>
37121934Sharti__FBSDID("$FreeBSD: head/sys/geom/geom_ctl.c 157581 2006-04-07 16:19:48Z marcel $");
38121934Sharti
39121934Sharti#include "opt_geom.h"
40121934Sharti
41121934Sharti#include <sys/param.h>
42121934Sharti#include <sys/systm.h>
43121934Sharti#include <sys/kernel.h>
44121934Sharti#include <sys/sysctl.h>
45121934Sharti#include <sys/bio.h>
46121934Sharti#include <sys/conf.h>
47121934Sharti#include <sys/disk.h>
48121934Sharti#include <sys/malloc.h>
49121934Sharti#include <sys/sysctl.h>
50121934Sharti#include <sys/sbuf.h>
51121934Sharti
52121934Sharti#include <sys/lock.h>
53121934Sharti#include <sys/mutex.h>
54121934Sharti
55121934Sharti#include <vm/vm.h>
56121934Sharti#include <vm/vm_extern.h>
57121934Sharti
58121934Sharti#include <geom/geom.h>
59121934Sharti#include <geom/geom_int.h>
60121934Sharti#define GCTL_TABLE 1
61121934Sharti#include <geom/geom_ctl.h>
62121934Sharti
63121934Sharti#include <machine/stdarg.h>
64121934Sharti
65121934Shartistatic d_ioctl_t g_ctl_ioctl;
66121934Sharti
67121934Shartistatic struct cdevsw g_ctl_cdevsw = {
68121934Sharti	.d_version =	D_VERSION,
69121934Sharti	.d_flags =	D_NEEDGIANT,
70121934Sharti	.d_ioctl =	g_ctl_ioctl,
71121934Sharti	.d_name =	"g_ctl",
72121934Sharti};
73121934Sharti
74121934Shartivoid
75121934Shartig_ctl_init(void)
76121934Sharti{
77121934Sharti
78121934Sharti	make_dev(&g_ctl_cdevsw, 0,
79121934Sharti	    UID_ROOT, GID_OPERATOR, 0640, PATH_GEOM_CTL);
80121934Sharti	KASSERT(GCTL_PARAM_RD == VM_PROT_READ,
81121934Sharti		("GCTL_PARAM_RD != VM_PROT_READ"));
82121934Sharti	KASSERT(GCTL_PARAM_WR == VM_PROT_WRITE,
83121934Sharti		("GCTL_PARAM_WR != VM_PROT_WRITE"));
84121934Sharti}
85
86/*
87 * Report an error back to the user in ascii format.  Return whatever copyout
88 * returned, or EINVAL if it succeeded.
89 */
90int
91gctl_error(struct gctl_req *req, const char *fmt, ...)
92{
93	va_list ap;
94
95	if (req == NULL)
96		return (EINVAL);
97
98	/* We only record the first error */
99	if (sbuf_done(req->serror)) {
100		if (!req->nerror)
101			req->nerror = EEXIST;
102	}
103	if (req->nerror)
104		return (req->nerror);
105
106	va_start(ap, fmt);
107	sbuf_vprintf(req->serror, fmt, ap);
108	va_end(ap);
109	sbuf_finish(req->serror);
110	if (g_debugflags & G_F_CTLDUMP)
111		printf("gctl %p error \"%s\"\n", req, sbuf_data(req->serror));
112	return (0);
113}
114
115/*
116 * Allocate space and copyin() something.
117 * XXX: this should really be a standard function in the kernel.
118 */
119static void *
120geom_alloc_copyin(struct gctl_req *req, void *uaddr, size_t len)
121{
122	void *ptr;
123
124	ptr = g_malloc(len, M_WAITOK);
125	if (ptr == NULL)
126		req->nerror = ENOMEM;
127	else
128		req->nerror = copyin(uaddr, ptr, len);
129	if (!req->nerror)
130		return (ptr);
131	if (ptr != NULL)
132		g_free(ptr);
133	return (NULL);
134}
135
136static void
137gctl_copyin(struct gctl_req *req)
138{
139	int error, i;
140	struct gctl_req_arg *ap;
141	char *p;
142
143	ap = geom_alloc_copyin(req, req->arg, req->narg * sizeof(*ap));
144	if (ap == NULL) {
145		req->nerror = ENOMEM;
146		req->arg = NULL;
147		return;
148	}
149
150	/* Nothing have been copyin()'ed yet */
151	for (i = 0; i < req->narg; i++) {
152		ap[i].flag &= ~(GCTL_PARAM_NAMEKERNEL|GCTL_PARAM_VALUEKERNEL);
153		ap[i].flag &= ~GCTL_PARAM_CHANGED;
154		ap[i].kvalue = NULL;
155	}
156
157	error = 0;
158	for (i = 0; i < req->narg; i++) {
159		if (ap[i].nlen < 1 || ap[i].nlen > SPECNAMELEN) {
160			error = gctl_error(req,
161			    "wrong param name length %d: %d", i, ap[i].nlen);
162			break;
163		}
164		p = geom_alloc_copyin(req, ap[i].name, ap[i].nlen);
165		if (p == NULL)
166			break;
167		if (p[ap[i].nlen - 1] != '\0') {
168			error = gctl_error(req, "unterminated param name");
169			g_free(p);
170			break;
171		}
172		ap[i].name = p;
173		ap[i].flag |= GCTL_PARAM_NAMEKERNEL;
174		if (ap[i].len <= 0) {
175			error = gctl_error(req, "negative param length");
176			break;
177		}
178		p = geom_alloc_copyin(req, ap[i].value, ap[i].len);
179		if (p == NULL)
180			break;
181		if ((ap[i].flag & GCTL_PARAM_ASCII) &&
182		    p[ap[i].len - 1] != '\0') {
183			error = gctl_error(req, "unterminated param value");
184			g_free(p);
185			break;
186		}
187		ap[i].kvalue = p;
188		ap[i].flag |= GCTL_PARAM_VALUEKERNEL;
189	}
190	req->arg = ap;
191	return;
192}
193
194static void
195gctl_copyout(struct gctl_req *req)
196{
197	int error, i;
198	struct gctl_req_arg *ap;
199
200	if (req->nerror)
201		return;
202	error = 0;
203	ap = req->arg;
204	for (i = 0; i < req->narg; i++, ap++) {
205		if (!(ap->flag & GCTL_PARAM_CHANGED))
206			continue;
207		error = copyout(ap->kvalue, ap->value, ap->len);
208		if (!error)
209			continue;
210		req->nerror = error;
211		return;
212	}
213	return;
214}
215
216static void
217gctl_free(struct gctl_req *req)
218{
219	int i;
220
221	if (req->arg == NULL)
222		return;
223	for (i = 0; i < req->narg; i++) {
224		if (req->arg[i].flag & GCTL_PARAM_NAMEKERNEL)
225			g_free(req->arg[i].name);
226		if ((req->arg[i].flag & GCTL_PARAM_VALUEKERNEL) &&
227		    req->arg[i].len > 0)
228			g_free(req->arg[i].kvalue);
229	}
230	g_free(req->arg);
231	sbuf_delete(req->serror);
232}
233
234static void
235gctl_dump(struct gctl_req *req)
236{
237	u_int i;
238	int j;
239	struct gctl_req_arg *ap;
240
241	printf("Dump of gctl request at %p:\n", req);
242	if (req->nerror > 0) {
243		printf("  nerror:\t%d\n", req->nerror);
244		if (sbuf_len(req->serror) > 0)
245			printf("  error:\t\"%s\"\n", sbuf_data(req->serror));
246	}
247	for (i = 0; i < req->narg; i++) {
248		ap = &req->arg[i];
249		if (!(ap->flag & GCTL_PARAM_NAMEKERNEL))
250			printf("  param:\t%d@%p", ap->nlen, ap->name);
251		else
252			printf("  param:\t\"%s\"", ap->name);
253		printf(" [%s%s%d] = ",
254		    ap->flag & GCTL_PARAM_RD ? "R" : "",
255		    ap->flag & GCTL_PARAM_WR ? "W" : "",
256		    ap->len);
257		if (!(ap->flag & GCTL_PARAM_VALUEKERNEL)) {
258			printf(" =@ %p", ap->value);
259		} else if (ap->flag & GCTL_PARAM_ASCII) {
260			printf("\"%s\"", (char *)ap->kvalue);
261		} else if (ap->len > 0) {
262			for (j = 0; j < ap->len && j < 512; j++)
263				printf(" %02x", ((u_char *)ap->kvalue)[j]);
264		} else {
265			printf(" = %p", ap->kvalue);
266		}
267		printf("\n");
268	}
269}
270
271int
272gctl_set_param(struct gctl_req *req, const char *param, void const *ptr,
273    int len)
274{
275	int i;
276	struct gctl_req_arg *ap;
277
278	for (i = 0; i < req->narg; i++) {
279		ap = &req->arg[i];
280		if (strcmp(param, ap->name))
281			continue;
282		if (!(ap->flag & GCTL_PARAM_WR))
283			return (EPERM);
284		ap->flag |= GCTL_PARAM_CHANGED;
285		if (ap->len < len) {
286			bcopy(ptr, ap->kvalue, ap->len);
287			return (ENOSPC);
288		}
289		bcopy(ptr, ap->kvalue, len);
290		return (0);
291	}
292	return (EINVAL);
293}
294
295void
296gctl_set_param_err(struct gctl_req *req, const char *param, void const *ptr,
297    int len)
298{
299
300	switch (gctl_set_param(req, param, ptr, len)) {
301	case EPERM:
302		gctl_error(req, "No write access %s argument", param);
303		break;
304	case ENOSPC:
305		gctl_error(req, "Wrong length %s argument", param);
306		break;
307	case EINVAL:
308		gctl_error(req, "Missing %s argument", param);
309		break;
310	}
311}
312
313void *
314gctl_get_param(struct gctl_req *req, const char *param, int *len)
315{
316	int i;
317	void *p;
318	struct gctl_req_arg *ap;
319
320	for (i = 0; i < req->narg; i++) {
321		ap = &req->arg[i];
322		if (strcmp(param, ap->name))
323			continue;
324		if (!(ap->flag & GCTL_PARAM_RD))
325			continue;
326		p = ap->kvalue;
327		if (len != NULL)
328			*len = ap->len;
329		return (p);
330	}
331	return (NULL);
332}
333
334char const *
335gctl_get_asciiparam(struct gctl_req *req, const char *param)
336{
337	int i;
338	char const *p;
339	struct gctl_req_arg *ap;
340
341	for (i = 0; i < req->narg; i++) {
342		ap = &req->arg[i];
343		if (strcmp(param, ap->name))
344			continue;
345		if (!(ap->flag & GCTL_PARAM_RD))
346			continue;
347		p = ap->kvalue;
348		if (ap->len < 1) {
349			gctl_error(req, "No length argument (%s)", param);
350			return (NULL);
351		}
352		if (p[ap->len - 1] != '\0') {
353			gctl_error(req, "Unterminated argument (%s)", param);
354			return (NULL);
355		}
356		return (p);
357	}
358	return (NULL);
359}
360
361void *
362gctl_get_paraml(struct gctl_req *req, const char *param, int len)
363{
364	int i;
365	void *p;
366
367	p = gctl_get_param(req, param, &i);
368	if (p == NULL)
369		gctl_error(req, "Missing %s argument", param);
370	else if (i != len) {
371		p = NULL;
372		gctl_error(req, "Wrong length %s argument", param);
373	}
374	return (p);
375}
376
377struct g_class *
378gctl_get_class(struct gctl_req *req, char const *arg)
379{
380	char const *p;
381	struct g_class *cp;
382
383	p = gctl_get_asciiparam(req, arg);
384	if (p == NULL)
385		return (NULL);
386	LIST_FOREACH(cp, &g_classes, class) {
387		if (!strcmp(p, cp->name))
388			return (cp);
389	}
390	return (NULL);
391}
392
393struct g_geom *
394gctl_get_geom(struct gctl_req *req, struct g_class *mpr, char const *arg)
395{
396	char const *p;
397	struct g_class *mp;
398	struct g_geom *gp;
399
400	p = gctl_get_asciiparam(req, arg);
401	if (p != NULL) {
402		LIST_FOREACH(mp, &g_classes, class) {
403			if (mpr != NULL && mpr != mp)
404				continue;
405			LIST_FOREACH(gp, &mp->geom, geom) {
406				if (!strcmp(p, gp->name))
407					return (gp);
408			}
409		}
410	}
411	gctl_error(req, "Geom not found");
412	return (NULL);
413}
414
415struct g_provider *
416gctl_get_provider(struct gctl_req *req, char const *arg)
417{
418	char const *p;
419	struct g_provider *pp;
420
421	p = gctl_get_asciiparam(req, arg);
422	if (p == NULL)
423		return (NULL);
424	pp = g_provider_by_name(p);
425	if (pp != NULL)
426		return (pp);
427	gctl_error(req, "Provider not found");
428	return (NULL);
429}
430
431static void
432g_ctl_req(void *arg, int flag __unused)
433{
434	struct g_class *mp;
435	struct gctl_req *req;
436	char const *verb;
437
438	g_topology_assert();
439	req = arg;
440	mp = gctl_get_class(req, "class");
441	if (mp == NULL) {
442		gctl_error(req, "Class not found");
443		return;
444	}
445	if (mp->ctlreq == NULL) {
446		gctl_error(req, "Class takes no requests");
447		return;
448	}
449	verb = gctl_get_param(req, "verb", NULL);
450	if (verb == NULL) {
451		gctl_error(req, "Verb missing");
452		return;
453	}
454	mp->ctlreq(req, mp, verb);
455	g_topology_assert();
456}
457
458
459static int
460g_ctl_ioctl_ctl(struct cdev *dev, u_long cmd, caddr_t data, int fflag, struct thread *td)
461{
462	struct gctl_req *req;
463	int nerror;
464
465	req = (void *)data;
466	req->nerror = 0;
467	req->serror = sbuf_new(NULL, NULL, 0, SBUF_AUTOEXTEND);
468	/* It is an error if we cannot return an error text */
469	if (req->lerror < 2)
470		return (EINVAL);
471	if (!useracc(req->error, req->lerror, VM_PROT_WRITE))
472		return (EINVAL);
473
474	/* Check the version */
475	if (req->version != GCTL_VERSION)
476		return (gctl_error(req,
477		    "kernel and libgeom version mismatch."));
478
479	/* Get things on board */
480	gctl_copyin(req);
481
482	if (g_debugflags & G_F_CTLDUMP)
483		gctl_dump(req);
484
485	if (!req->nerror) {
486		g_waitfor_event(g_ctl_req, req, M_WAITOK, NULL);
487		gctl_copyout(req);
488	}
489	if (sbuf_done(req->serror)) {
490		req->nerror = copyout(sbuf_data(req->serror), req->error,
491		    imin(req->lerror, sbuf_len(req->serror) + 1));
492	}
493
494	nerror = req->nerror;
495	gctl_free(req);
496	return (nerror);
497}
498
499static int
500g_ctl_ioctl(struct cdev *dev, u_long cmd, caddr_t data, int fflag, struct thread *td)
501{
502	int error;
503
504	switch(cmd) {
505	case GEOM_CTL:
506		error = g_ctl_ioctl_ctl(dev, cmd, data, fflag, td);
507		break;
508	default:
509		error = ENOIOCTL;
510		break;
511	}
512	return (error);
513
514}
515