geom_ctl.c revision 157581
1189251Ssam/*-
2189251Ssam * Copyright (c) 2002 Poul-Henning Kamp
3189251Ssam * Copyright (c) 2002 Networks Associates Technology, Inc.
4189251Ssam * All rights reserved.
5252726Srpaulo *
6252726Srpaulo * This software was developed for the FreeBSD Project by Poul-Henning Kamp
7189251Ssam * and NAI Labs, the Security Research Division of Network Associates, Inc.
8189251Ssam * under DARPA/SPAWAR contract N66001-01-C-8035 ("CBOSS"), as part of the
9189251Ssam * DARPA CHATS research program.
10189251Ssam *
11189251Ssam * Redistribution and use in source and binary forms, with or without
12214734Srpaulo * modification, are permitted provided that the following conditions
13252726Srpaulo * are met:
14189251Ssam * 1. Redistributions of source code must retain the above copyright
15189251Ssam *    notice, this list of conditions and the following disclaimer.
16189251Ssam * 2. Redistributions in binary form must reproduce the above copyright
17189251Ssam *    notice, this list of conditions and the following disclaimer in the
18189251Ssam *    documentation and/or other materials provided with the distribution.
19189251Ssam * 3. The names of the authors may not be used to endorse or promote
20189251Ssam *    products derived from this software without specific prior written
21189251Ssam *    permission.
22189251Ssam *
23189251Ssam * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
24189251Ssam * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
25189251Ssam * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
26189251Ssam * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
27189251Ssam * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
28189251Ssam * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
29189251Ssam * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
30189251Ssam * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
31189251Ssam * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
32189251Ssam * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
33189251Ssam * SUCH DAMAGE.
34189251Ssam */
35189251Ssam
36189251Ssam#include <sys/cdefs.h>
37189251Ssam__FBSDID("$FreeBSD: head/sys/geom/geom_ctl.c 157581 2006-04-07 16:19:48Z marcel $");
38189251Ssam
39189251Ssam#include "opt_geom.h"
40189251Ssam
41189251Ssam#include <sys/param.h>
42189251Ssam#include <sys/systm.h>
43189251Ssam#include <sys/kernel.h>
44189251Ssam#include <sys/sysctl.h>
45189251Ssam#include <sys/bio.h>
46189251Ssam#include <sys/conf.h>
47189251Ssam#include <sys/disk.h>
48189251Ssam#include <sys/malloc.h>
49189251Ssam#include <sys/sysctl.h>
50189251Ssam#include <sys/sbuf.h>
51189251Ssam
52189251Ssam#include <sys/lock.h>
53189251Ssam#include <sys/mutex.h>
54189251Ssam
55189251Ssam#include <vm/vm.h>
56189251Ssam#include <vm/vm_extern.h>
57189251Ssam
58189251Ssam#include <geom/geom.h>
59189251Ssam#include <geom/geom_int.h>
60189251Ssam#define GCTL_TABLE 1
61189251Ssam#include <geom/geom_ctl.h>
62189251Ssam
63189251Ssam#include <machine/stdarg.h>
64189251Ssam
65189251Ssamstatic d_ioctl_t g_ctl_ioctl;
66189251Ssam
67189251Ssamstatic struct cdevsw g_ctl_cdevsw = {
68189251Ssam	.d_version =	D_VERSION,
69189251Ssam	.d_flags =	D_NEEDGIANT,
70189251Ssam	.d_ioctl =	g_ctl_ioctl,
71189251Ssam	.d_name =	"g_ctl",
72189251Ssam};
73189251Ssam
74189251Ssamvoid
75189251Ssamg_ctl_init(void)
76189251Ssam{
77189251Ssam
78189251Ssam	make_dev(&g_ctl_cdevsw, 0,
79189251Ssam	    UID_ROOT, GID_OPERATOR, 0640, PATH_GEOM_CTL);
80189251Ssam	KASSERT(GCTL_PARAM_RD == VM_PROT_READ,
81189251Ssam		("GCTL_PARAM_RD != VM_PROT_READ"));
82189251Ssam	KASSERT(GCTL_PARAM_WR == VM_PROT_WRITE,
83189251Ssam		("GCTL_PARAM_WR != VM_PROT_WRITE"));
84189251Ssam}
85189251Ssam
86189251Ssam/*
87189251Ssam * Report an error back to the user in ascii format.  Return whatever copyout
88189251Ssam * returned, or EINVAL if it succeeded.
89189251Ssam */
90189251Ssamint
91189251Ssamgctl_error(struct gctl_req *req, const char *fmt, ...)
92189251Ssam{
93189251Ssam	va_list ap;
94189251Ssam
95189251Ssam	if (req == NULL)
96189251Ssam		return (EINVAL);
97189251Ssam
98189251Ssam	/* We only record the first error */
99189251Ssam	if (sbuf_done(req->serror)) {
100189251Ssam		if (!req->nerror)
101189251Ssam			req->nerror = EEXIST;
102189251Ssam	}
103189251Ssam	if (req->nerror)
104189251Ssam		return (req->nerror);
105189251Ssam
106189251Ssam	va_start(ap, fmt);
107189251Ssam	sbuf_vprintf(req->serror, fmt, ap);
108189251Ssam	va_end(ap);
109189251Ssam	sbuf_finish(req->serror);
110189251Ssam	if (g_debugflags & G_F_CTLDUMP)
111189251Ssam		printf("gctl %p error \"%s\"\n", req, sbuf_data(req->serror));
112189251Ssam	return (0);
113189251Ssam}
114189251Ssam
115189251Ssam/*
116189251Ssam * Allocate space and copyin() something.
117189251Ssam * XXX: this should really be a standard function in the kernel.
118189251Ssam */
119189251Ssamstatic void *
120189251Ssamgeom_alloc_copyin(struct gctl_req *req, void *uaddr, size_t len)
121189251Ssam{
122189251Ssam	void *ptr;
123189251Ssam
124189251Ssam	ptr = g_malloc(len, M_WAITOK);
125189251Ssam	if (ptr == NULL)
126189251Ssam		req->nerror = ENOMEM;
127189251Ssam	else
128189251Ssam		req->nerror = copyin(uaddr, ptr, len);
129189251Ssam	if (!req->nerror)
130189251Ssam		return (ptr);
131189251Ssam	if (ptr != NULL)
132189251Ssam		g_free(ptr);
133189251Ssam	return (NULL);
134189251Ssam}
135189251Ssam
136189251Ssamstatic void
137189251Ssamgctl_copyin(struct gctl_req *req)
138189251Ssam{
139189251Ssam	int error, i;
140189251Ssam	struct gctl_req_arg *ap;
141189251Ssam	char *p;
142189251Ssam
143189251Ssam	ap = geom_alloc_copyin(req, req->arg, req->narg * sizeof(*ap));
144189251Ssam	if (ap == NULL) {
145189251Ssam		req->nerror = ENOMEM;
146189251Ssam		req->arg = NULL;
147189251Ssam		return;
148189251Ssam	}
149189251Ssam
150189251Ssam	/* Nothing have been copyin()'ed yet */
151189251Ssam	for (i = 0; i < req->narg; i++) {
152189251Ssam		ap[i].flag &= ~(GCTL_PARAM_NAMEKERNEL|GCTL_PARAM_VALUEKERNEL);
153189251Ssam		ap[i].flag &= ~GCTL_PARAM_CHANGED;
154189251Ssam		ap[i].kvalue = NULL;
155189251Ssam	}
156189251Ssam
157189251Ssam	error = 0;
158189251Ssam	for (i = 0; i < req->narg; i++) {
159189251Ssam		if (ap[i].nlen < 1 || ap[i].nlen > SPECNAMELEN) {
160189251Ssam			error = gctl_error(req,
161189251Ssam			    "wrong param name length %d: %d", i, ap[i].nlen);
162189251Ssam			break;
163189251Ssam		}
164189251Ssam		p = geom_alloc_copyin(req, ap[i].name, ap[i].nlen);
165189251Ssam		if (p == NULL)
166189251Ssam			break;
167189251Ssam		if (p[ap[i].nlen - 1] != '\0') {
168189251Ssam			error = gctl_error(req, "unterminated param name");
169189251Ssam			g_free(p);
170189251Ssam			break;
171189251Ssam		}
172189251Ssam		ap[i].name = p;
173189251Ssam		ap[i].flag |= GCTL_PARAM_NAMEKERNEL;
174189251Ssam		if (ap[i].len <= 0) {
175189251Ssam			error = gctl_error(req, "negative param length");
176189251Ssam			break;
177189251Ssam		}
178189251Ssam		p = geom_alloc_copyin(req, ap[i].value, ap[i].len);
179189251Ssam		if (p == NULL)
180189251Ssam			break;
181189251Ssam		if ((ap[i].flag & GCTL_PARAM_ASCII) &&
182189251Ssam		    p[ap[i].len - 1] != '\0') {
183189251Ssam			error = gctl_error(req, "unterminated param value");
184189251Ssam			g_free(p);
185189251Ssam			break;
186189251Ssam		}
187189251Ssam		ap[i].kvalue = p;
188189251Ssam		ap[i].flag |= GCTL_PARAM_VALUEKERNEL;
189189251Ssam	}
190189251Ssam	req->arg = ap;
191189251Ssam	return;
192189251Ssam}
193189251Ssam
194189251Ssamstatic void
195189251Ssamgctl_copyout(struct gctl_req *req)
196189251Ssam{
197189251Ssam	int error, i;
198189251Ssam	struct gctl_req_arg *ap;
199189251Ssam
200189251Ssam	if (req->nerror)
201189251Ssam		return;
202189251Ssam	error = 0;
203189251Ssam	ap = req->arg;
204189251Ssam	for (i = 0; i < req->narg; i++, ap++) {
205189251Ssam		if (!(ap->flag & GCTL_PARAM_CHANGED))
206189251Ssam			continue;
207189251Ssam		error = copyout(ap->kvalue, ap->value, ap->len);
208189251Ssam		if (!error)
209189251Ssam			continue;
210189251Ssam		req->nerror = error;
211189251Ssam		return;
212189251Ssam	}
213189251Ssam	return;
214189251Ssam}
215189251Ssam
216189251Ssamstatic void
217189251Ssamgctl_free(struct gctl_req *req)
218189251Ssam{
219189251Ssam	int i;
220189251Ssam
221189251Ssam	if (req->arg == NULL)
222189251Ssam		return;
223189251Ssam	for (i = 0; i < req->narg; i++) {
224189251Ssam		if (req->arg[i].flag & GCTL_PARAM_NAMEKERNEL)
225189251Ssam			g_free(req->arg[i].name);
226189251Ssam		if ((req->arg[i].flag & GCTL_PARAM_VALUEKERNEL) &&
227189251Ssam		    req->arg[i].len > 0)
228189251Ssam			g_free(req->arg[i].kvalue);
229189251Ssam	}
230189251Ssam	g_free(req->arg);
231189251Ssam	sbuf_delete(req->serror);
232189251Ssam}
233189251Ssam
234189251Ssamstatic void
235189251Ssamgctl_dump(struct gctl_req *req)
236189251Ssam{
237189251Ssam	u_int i;
238189251Ssam	int j;
239189251Ssam	struct gctl_req_arg *ap;
240189251Ssam
241189251Ssam	printf("Dump of gctl request at %p:\n", req);
242189251Ssam	if (req->nerror > 0) {
243189251Ssam		printf("  nerror:\t%d\n", req->nerror);
244189251Ssam		if (sbuf_len(req->serror) > 0)
245189251Ssam			printf("  error:\t\"%s\"\n", sbuf_data(req->serror));
246189251Ssam	}
247189251Ssam	for (i = 0; i < req->narg; i++) {
248189251Ssam		ap = &req->arg[i];
249189251Ssam		if (!(ap->flag & GCTL_PARAM_NAMEKERNEL))
250189251Ssam			printf("  param:\t%d@%p", ap->nlen, ap->name);
251189251Ssam		else
252189251Ssam			printf("  param:\t\"%s\"", ap->name);
253189251Ssam		printf(" [%s%s%d] = ",
254189251Ssam		    ap->flag & GCTL_PARAM_RD ? "R" : "",
255189251Ssam		    ap->flag & GCTL_PARAM_WR ? "W" : "",
256189251Ssam		    ap->len);
257189251Ssam		if (!(ap->flag & GCTL_PARAM_VALUEKERNEL)) {
258189251Ssam			printf(" =@ %p", ap->value);
259189251Ssam		} else if (ap->flag & GCTL_PARAM_ASCII) {
260189251Ssam			printf("\"%s\"", (char *)ap->kvalue);
261189251Ssam		} else if (ap->len > 0) {
262189251Ssam			for (j = 0; j < ap->len && j < 512; j++)
263189251Ssam				printf(" %02x", ((u_char *)ap->kvalue)[j]);
264189251Ssam		} else {
265189251Ssam			printf(" = %p", ap->kvalue);
266189251Ssam		}
267189251Ssam		printf("\n");
268189251Ssam	}
269189251Ssam}
270189251Ssam
271189251Ssamint
272189251Ssamgctl_set_param(struct gctl_req *req, const char *param, void const *ptr,
273189251Ssam    int len)
274189251Ssam{
275189251Ssam	int i;
276189251Ssam	struct gctl_req_arg *ap;
277189251Ssam
278189251Ssam	for (i = 0; i < req->narg; i++) {
279189251Ssam		ap = &req->arg[i];
280189251Ssam		if (strcmp(param, ap->name))
281189251Ssam			continue;
282189251Ssam		if (!(ap->flag & GCTL_PARAM_WR))
283189251Ssam			return (EPERM);
284189251Ssam		ap->flag |= GCTL_PARAM_CHANGED;
285189251Ssam		if (ap->len < len) {
286189251Ssam			bcopy(ptr, ap->kvalue, ap->len);
287189251Ssam			return (ENOSPC);
288189251Ssam		}
289189251Ssam		bcopy(ptr, ap->kvalue, len);
290189251Ssam		return (0);
291189251Ssam	}
292189251Ssam	return (EINVAL);
293189251Ssam}
294189251Ssam
295189251Ssamvoid
296189251Ssamgctl_set_param_err(struct gctl_req *req, const char *param, void const *ptr,
297189251Ssam    int len)
298189251Ssam{
299189251Ssam
300189251Ssam	switch (gctl_set_param(req, param, ptr, len)) {
301189251Ssam	case EPERM:
302189251Ssam		gctl_error(req, "No write access %s argument", param);
303189251Ssam		break;
304189251Ssam	case ENOSPC:
305189251Ssam		gctl_error(req, "Wrong length %s argument", param);
306189251Ssam		break;
307189251Ssam	case EINVAL:
308189251Ssam		gctl_error(req, "Missing %s argument", param);
309189251Ssam		break;
310189251Ssam	}
311189251Ssam}
312189251Ssam
313189251Ssamvoid *
314189251Ssamgctl_get_param(struct gctl_req *req, const char *param, int *len)
315189251Ssam{
316189251Ssam	int i;
317189251Ssam	void *p;
318189251Ssam	struct gctl_req_arg *ap;
319189251Ssam
320189251Ssam	for (i = 0; i < req->narg; i++) {
321189251Ssam		ap = &req->arg[i];
322189251Ssam		if (strcmp(param, ap->name))
323189251Ssam			continue;
324189251Ssam		if (!(ap->flag & GCTL_PARAM_RD))
325189251Ssam			continue;
326189251Ssam		p = ap->kvalue;
327189251Ssam		if (len != NULL)
328189251Ssam			*len = ap->len;
329189251Ssam		return (p);
330189251Ssam	}
331189251Ssam	return (NULL);
332189251Ssam}
333189251Ssam
334189251Ssamchar const *
335189251Ssamgctl_get_asciiparam(struct gctl_req *req, const char *param)
336189251Ssam{
337189251Ssam	int i;
338189251Ssam	char const *p;
339189251Ssam	struct gctl_req_arg *ap;
340189251Ssam
341189251Ssam	for (i = 0; i < req->narg; i++) {
342189251Ssam		ap = &req->arg[i];
343189251Ssam		if (strcmp(param, ap->name))
344189251Ssam			continue;
345189251Ssam		if (!(ap->flag & GCTL_PARAM_RD))
346189251Ssam			continue;
347189251Ssam		p = ap->kvalue;
348189251Ssam		if (ap->len < 1) {
349189251Ssam			gctl_error(req, "No length argument (%s)", param);
350189251Ssam			return (NULL);
351189251Ssam		}
352189251Ssam		if (p[ap->len - 1] != '\0') {
353189251Ssam			gctl_error(req, "Unterminated argument (%s)", param);
354189251Ssam			return (NULL);
355189251Ssam		}
356189251Ssam		return (p);
357189251Ssam	}
358189251Ssam	return (NULL);
359189251Ssam}
360189251Ssam
361189251Ssamvoid *
362189251Ssamgctl_get_paraml(struct gctl_req *req, const char *param, int len)
363189251Ssam{
364189251Ssam	int i;
365189251Ssam	void *p;
366189251Ssam
367189251Ssam	p = gctl_get_param(req, param, &i);
368189251Ssam	if (p == NULL)
369189251Ssam		gctl_error(req, "Missing %s argument", param);
370189251Ssam	else if (i != len) {
371189251Ssam		p = NULL;
372189251Ssam		gctl_error(req, "Wrong length %s argument", param);
373189251Ssam	}
374189251Ssam	return (p);
375189251Ssam}
376189251Ssam
377189251Ssamstruct g_class *
378189251Ssamgctl_get_class(struct gctl_req *req, char const *arg)
379189251Ssam{
380189251Ssam	char const *p;
381189251Ssam	struct g_class *cp;
382189251Ssam
383189251Ssam	p = gctl_get_asciiparam(req, arg);
384189251Ssam	if (p == NULL)
385189251Ssam		return (NULL);
386189251Ssam	LIST_FOREACH(cp, &g_classes, class) {
387189251Ssam		if (!strcmp(p, cp->name))
388189251Ssam			return (cp);
389189251Ssam	}
390189251Ssam	return (NULL);
391189251Ssam}
392189251Ssam
393189251Ssamstruct g_geom *
394189251Ssamgctl_get_geom(struct gctl_req *req, struct g_class *mpr, char const *arg)
395189251Ssam{
396189251Ssam	char const *p;
397189251Ssam	struct g_class *mp;
398189251Ssam	struct g_geom *gp;
399189251Ssam
400189251Ssam	p = gctl_get_asciiparam(req, arg);
401189251Ssam	if (p != NULL) {
402189251Ssam		LIST_FOREACH(mp, &g_classes, class) {
403189251Ssam			if (mpr != NULL && mpr != mp)
404189251Ssam				continue;
405189251Ssam			LIST_FOREACH(gp, &mp->geom, geom) {
406189251Ssam				if (!strcmp(p, gp->name))
407189251Ssam					return (gp);
408189251Ssam			}
409189251Ssam		}
410189251Ssam	}
411189251Ssam	gctl_error(req, "Geom not found");
412189251Ssam	return (NULL);
413189251Ssam}
414189251Ssam
415189251Ssamstruct g_provider *
416189251Ssamgctl_get_provider(struct gctl_req *req, char const *arg)
417189251Ssam{
418189251Ssam	char const *p;
419189251Ssam	struct g_provider *pp;
420189251Ssam
421189251Ssam	p = gctl_get_asciiparam(req, arg);
422252726Srpaulo	if (p == NULL)
423189251Ssam		return (NULL);
424189251Ssam	pp = g_provider_by_name(p);
425189251Ssam	if (pp != NULL)
426189251Ssam		return (pp);
427189251Ssam	gctl_error(req, "Provider not found");
428189251Ssam	return (NULL);
429189251Ssam}
430189251Ssam
431189251Ssamstatic void
432189251Ssamg_ctl_req(void *arg, int flag __unused)
433189251Ssam{
434189251Ssam	struct g_class *mp;
435189251Ssam	struct gctl_req *req;
436189251Ssam	char const *verb;
437189251Ssam
438189251Ssam	g_topology_assert();
439189251Ssam	req = arg;
440189251Ssam	mp = gctl_get_class(req, "class");
441189251Ssam	if (mp == NULL) {
442189251Ssam		gctl_error(req, "Class not found");
443189251Ssam		return;
444189251Ssam	}
445189251Ssam	if (mp->ctlreq == NULL) {
446189251Ssam		gctl_error(req, "Class takes no requests");
447189251Ssam		return;
448189251Ssam	}
449189251Ssam	verb = gctl_get_param(req, "verb", NULL);
450189251Ssam	if (verb == NULL) {
451189251Ssam		gctl_error(req, "Verb missing");
452189251Ssam		return;
453189251Ssam	}
454189251Ssam	mp->ctlreq(req, mp, verb);
455189251Ssam	g_topology_assert();
456189251Ssam}
457189251Ssam
458189251Ssam
459189251Ssamstatic int
460189251Ssamg_ctl_ioctl_ctl(struct cdev *dev, u_long cmd, caddr_t data, int fflag, struct thread *td)
461189251Ssam{
462189251Ssam	struct gctl_req *req;
463189251Ssam	int nerror;
464189251Ssam
465189251Ssam	req = (void *)data;
466189251Ssam	req->nerror = 0;
467189251Ssam	req->serror = sbuf_new(NULL, NULL, 0, SBUF_AUTOEXTEND);
468189251Ssam	/* It is an error if we cannot return an error text */
469189251Ssam	if (req->lerror < 2)
470189251Ssam		return (EINVAL);
471189251Ssam	if (!useracc(req->error, req->lerror, VM_PROT_WRITE))
472189251Ssam		return (EINVAL);
473189251Ssam
474189251Ssam	/* Check the version */
475189251Ssam	if (req->version != GCTL_VERSION)
476189251Ssam		return (gctl_error(req,
477189251Ssam		    "kernel and libgeom version mismatch."));
478189251Ssam
479189251Ssam	/* Get things on board */
480189251Ssam	gctl_copyin(req);
481189251Ssam
482189251Ssam	if (g_debugflags & G_F_CTLDUMP)
483189251Ssam		gctl_dump(req);
484189251Ssam
485189251Ssam	if (!req->nerror) {
486189251Ssam		g_waitfor_event(g_ctl_req, req, M_WAITOK, NULL);
487189251Ssam		gctl_copyout(req);
488189251Ssam	}
489189251Ssam	if (sbuf_done(req->serror)) {
490189251Ssam		req->nerror = copyout(sbuf_data(req->serror), req->error,
491189251Ssam		    imin(req->lerror, sbuf_len(req->serror) + 1));
492189251Ssam	}
493189251Ssam
494189251Ssam	nerror = req->nerror;
495189251Ssam	gctl_free(req);
496189251Ssam	return (nerror);
497189251Ssam}
498189251Ssam
499189251Ssamstatic int
500189251Ssamg_ctl_ioctl(struct cdev *dev, u_long cmd, caddr_t data, int fflag, struct thread *td)
501189251Ssam{
502189251Ssam	int error;
503189251Ssam
504189251Ssam	switch(cmd) {
505189251Ssam	case GEOM_CTL:
506189251Ssam		error = g_ctl_ioctl_ctl(dev, cmd, data, fflag, td);
507189251Ssam		break;
508189251Ssam	default:
509189251Ssam		error = ENOIOCTL;
510189251Ssam		break;
511189251Ssam	}
512189251Ssam	return (error);
513189251Ssam
514189251Ssam}
515189251Ssam