geom_ctl.c revision 150304
1105068Sphk/*-
2105068Sphk * Copyright (c) 2002 Poul-Henning Kamp
3105068Sphk * Copyright (c) 2002 Networks Associates Technology, Inc.
4105068Sphk * All rights reserved.
5105068Sphk *
6105068Sphk * This software was developed for the FreeBSD Project by Poul-Henning Kamp
7105068Sphk * and NAI Labs, the Security Research Division of Network Associates, Inc.
8105068Sphk * under DARPA/SPAWAR contract N66001-01-C-8035 ("CBOSS"), as part of the
9105068Sphk * DARPA CHATS research program.
10105068Sphk *
11105068Sphk * Redistribution and use in source and binary forms, with or without
12105068Sphk * modification, are permitted provided that the following conditions
13105068Sphk * are met:
14105068Sphk * 1. Redistributions of source code must retain the above copyright
15105068Sphk *    notice, this list of conditions and the following disclaimer.
16105068Sphk * 2. Redistributions in binary form must reproduce the above copyright
17105068Sphk *    notice, this list of conditions and the following disclaimer in the
18105068Sphk *    documentation and/or other materials provided with the distribution.
19105068Sphk * 3. The names of the authors may not be used to endorse or promote
20105068Sphk *    products derived from this software without specific prior written
21105068Sphk *    permission.
22105068Sphk *
23105068Sphk * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
24105068Sphk * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
25105068Sphk * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
26105068Sphk * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
27105068Sphk * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
28105068Sphk * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
29105068Sphk * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
30105068Sphk * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
31105068Sphk * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
32105068Sphk * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
33105068Sphk * SUCH DAMAGE.
34105068Sphk */
35105068Sphk
36116196Sobrien#include <sys/cdefs.h>
37116196Sobrien__FBSDID("$FreeBSD: head/sys/geom/geom_ctl.c 150304 2005-09-18 23:54:40Z marcel $");
38116196Sobrien
39105068Sphk#include "opt_geom.h"
40105068Sphk
41105068Sphk#include <sys/param.h>
42105068Sphk#include <sys/systm.h>
43105068Sphk#include <sys/kernel.h>
44105068Sphk#include <sys/sysctl.h>
45105068Sphk#include <sys/bio.h>
46105068Sphk#include <sys/conf.h>
47105068Sphk#include <sys/disk.h>
48105068Sphk#include <sys/malloc.h>
49105068Sphk#include <sys/sysctl.h>
50113892Sphk#include <sys/sbuf.h>
51105068Sphk
52105068Sphk#include <sys/lock.h>
53105068Sphk#include <sys/mutex.h>
54112511Sphk
55112511Sphk#include <vm/vm.h>
56112511Sphk#include <vm/vm_extern.h>
57112511Sphk
58105068Sphk#include <geom/geom.h>
59105068Sphk#include <geom/geom_int.h>
60112709Sphk#define GCTL_TABLE 1
61112511Sphk#include <geom/geom_ctl.h>
62105068Sphk
63113892Sphk#include <machine/stdarg.h>
64113892Sphk
65105068Sphkstatic d_ioctl_t g_ctl_ioctl;
66105068Sphk
67112534Sphkstatic struct cdevsw g_ctl_cdevsw = {
68126080Sphk	.d_version =	D_VERSION,
69126080Sphk	.d_flags =	D_NEEDGIANT,
70112534Sphk	.d_ioctl =	g_ctl_ioctl,
71112534Sphk	.d_name =	"g_ctl",
72105068Sphk};
73105068Sphk
74112534Sphkvoid
75105068Sphkg_ctl_init(void)
76105068Sphk{
77105068Sphk
78112534Sphk	make_dev(&g_ctl_cdevsw, 0,
79112534Sphk	    UID_ROOT, GID_OPERATOR, 0640, PATH_GEOM_CTL);
80112709Sphk	KASSERT(GCTL_PARAM_RD == VM_PROT_READ,
81112709Sphk		("GCTL_PARAM_RD != VM_PROT_READ"));
82112709Sphk	KASSERT(GCTL_PARAM_WR == VM_PROT_WRITE,
83112709Sphk		("GCTL_PARAM_WR != VM_PROT_WRITE"));
84105068Sphk}
85105068Sphk
86112511Sphk/*
87112511Sphk * Report an error back to the user in ascii format.  Return whatever copyout
88112511Sphk * returned, or EINVAL if it succeeded.
89112511Sphk */
90112709Sphkint
91113892Sphkgctl_error(struct gctl_req *req, const char *fmt, ...)
92112511Sphk{
93113892Sphk	va_list ap;
94112511Sphk
95115624Sphk	if (req == NULL)
96115624Sphk		return (EINVAL);
97115624Sphk
98115624Sphk	/* We only record the first error */
99144789Spjd	if (sbuf_done(req->serror)) {
100144789Spjd		if (!req->nerror)
101144789Spjd			req->nerror = EEXIST;
102144789Spjd	}
103115624Sphk	if (req->nerror)
104115624Sphk		return (req->nerror);
105115624Sphk
106115624Sphk	va_start(ap, fmt);
107115624Sphk	sbuf_vprintf(req->serror, fmt, ap);
108115949Sphk	va_end(ap);
109115624Sphk	sbuf_finish(req->serror);
110115624Sphk	if (g_debugflags & G_F_CTLDUMP)
111115624Sphk		printf("gctl %p error \"%s\"\n", req, sbuf_data(req->serror));
112144789Spjd	return (0);
113112511Sphk}
114112511Sphk
115112511Sphk/*
116112511Sphk * Allocate space and copyin() something.
117112511Sphk * XXX: this should really be a standard function in the kernel.
118112511Sphk */
119112511Sphkstatic void *
120115624Sphkgeom_alloc_copyin(struct gctl_req *req, void *uaddr, size_t len)
121112511Sphk{
122112511Sphk	void *ptr;
123112511Sphk
124112511Sphk	ptr = g_malloc(len, M_WAITOK);
125112511Sphk	if (ptr == NULL)
126115624Sphk		req->nerror = ENOMEM;
127112511Sphk	else
128115624Sphk		req->nerror = copyin(uaddr, ptr, len);
129115624Sphk	if (!req->nerror)
130112511Sphk		return (ptr);
131112511Sphk	if (ptr != NULL)
132112511Sphk		g_free(ptr);
133112511Sphk	return (NULL);
134112511Sphk}
135112511Sphk
136115624Sphkstatic void
137112709Sphkgctl_copyin(struct gctl_req *req)
138112511Sphk{
139115624Sphk	int error, i;
140112709Sphk	struct gctl_req_arg *ap;
141112511Sphk	char *p;
142112511Sphk
143115624Sphk	ap = geom_alloc_copyin(req, req->arg, req->narg * sizeof(*ap));
144112709Sphk	if (ap == NULL) {
145115624Sphk		req->nerror = ENOMEM;
146115624Sphk		req->arg = NULL;
147115624Sphk		return;
148112709Sphk	}
149112709Sphk
150115624Sphk	/* Nothing have been copyin()'ed yet */
151115624Sphk	for (i = 0; i < req->narg; i++) {
152115624Sphk		ap[i].flag &= ~(GCTL_PARAM_NAMEKERNEL|GCTL_PARAM_VALUEKERNEL);
153115624Sphk		ap[i].flag &= ~GCTL_PARAM_CHANGED;
154115624Sphk		ap[i].kvalue = NULL;
155115624Sphk	}
156115624Sphk
157115624Sphk	error = 0;
158115624Sphk	for (i = 0; i < req->narg; i++) {
159112709Sphk		if (ap[i].nlen < 1 || ap[i].nlen > SPECNAMELEN) {
160115624Sphk			error = gctl_error(req,
161115624Sphk			    "wrong param name length %d: %d", i, ap[i].nlen);
162112511Sphk			break;
163112709Sphk		}
164115624Sphk		p = geom_alloc_copyin(req, ap[i].name, ap[i].nlen);
165112709Sphk		if (p == NULL)
166112511Sphk			break;
167112709Sphk		if (p[ap[i].nlen - 1] != '\0') {
168112709Sphk			error = gctl_error(req, "unterminated param name");
169112511Sphk			g_free(p);
170112511Sphk			break;
171112511Sphk		}
172112709Sphk		ap[i].name = p;
173115624Sphk		ap[i].flag |= GCTL_PARAM_NAMEKERNEL;
174140367Sphk		if (ap[i].len <= 0) {
175115624Sphk			error = gctl_error(req, "negative param length");
176115624Sphk			break;
177115624Sphk		}
178115624Sphk		p = geom_alloc_copyin(req, ap[i].value, ap[i].len);
179115624Sphk		if (p == NULL)
180115624Sphk			break;
181115624Sphk		if ((ap[i].flag & GCTL_PARAM_ASCII) &&
182115624Sphk		    p[ap[i].len - 1] != '\0') {
183115624Sphk			error = gctl_error(req, "unterminated param value");
184115624Sphk			g_free(p);
185115624Sphk			break;
186115624Sphk		}
187115624Sphk		ap[i].kvalue = p;
188115624Sphk		ap[i].flag |= GCTL_PARAM_VALUEKERNEL;
189112511Sphk	}
190115624Sphk	req->arg = ap;
191115624Sphk	return;
192115624Sphk}
193115624Sphk
194115624Sphkstatic void
195115624Sphkgctl_copyout(struct gctl_req *req)
196115624Sphk{
197115624Sphk	int error, i;
198115624Sphk	struct gctl_req_arg *ap;
199115624Sphk
200115624Sphk	if (req->nerror)
201115624Sphk		return;
202115624Sphk	error = 0;
203115624Sphk	ap = req->arg;
204115624Sphk	for (i = 0; i < req->narg; i++, ap++) {
205115624Sphk		if (!(ap->flag & GCTL_PARAM_CHANGED))
206115624Sphk			continue;
207115624Sphk		error = copyout(ap->kvalue, ap->value, ap->len);
208115624Sphk		if (!error)
209115624Sphk			continue;
210115624Sphk		req->nerror = error;
211115624Sphk		return;
212112511Sphk	}
213115624Sphk	return;
214112511Sphk}
215112511Sphk
216112511Sphkstatic void
217114531Sphkgctl_free(struct gctl_req *req)
218114531Sphk{
219114531Sphk	int i;
220114531Sphk
221115624Sphk	if (req->arg == NULL)
222115624Sphk		return;
223114531Sphk	for (i = 0; i < req->narg; i++) {
224115624Sphk		if (req->arg[i].flag & GCTL_PARAM_NAMEKERNEL)
225114531Sphk			g_free(req->arg[i].name);
226115624Sphk		if ((req->arg[i].flag & GCTL_PARAM_VALUEKERNEL) &&
227115624Sphk		    req->arg[i].len > 0)
228115624Sphk			g_free(req->arg[i].kvalue);
229114531Sphk	}
230114531Sphk	g_free(req->arg);
231115624Sphk	sbuf_delete(req->serror);
232114531Sphk}
233114531Sphk
234114531Sphkstatic void
235112709Sphkgctl_dump(struct gctl_req *req)
236112511Sphk{
237112511Sphk	u_int i;
238115624Sphk	int j;
239112709Sphk	struct gctl_req_arg *ap;
240112511Sphk
241115624Sphk	printf("Dump of gctl request at %p:\n", req);
242115624Sphk	if (req->nerror > 0) {
243115624Sphk		printf("  nerror:\t%d\n", req->nerror);
244115624Sphk		if (sbuf_len(req->serror) > 0)
245115624Sphk			printf("  error:\t\"%s\"\n", sbuf_data(req->serror));
246112511Sphk	}
247112511Sphk	for (i = 0; i < req->narg; i++) {
248112511Sphk		ap = &req->arg[i];
249115624Sphk		if (!(ap->flag & GCTL_PARAM_NAMEKERNEL))
250115624Sphk			printf("  param:\t%d@%p", ap->nlen, ap->name);
251115624Sphk		else
252115624Sphk			printf("  param:\t\"%s\"", ap->name);
253112709Sphk		printf(" [%s%s%d] = ",
254112709Sphk		    ap->flag & GCTL_PARAM_RD ? "R" : "",
255112709Sphk		    ap->flag & GCTL_PARAM_WR ? "W" : "",
256112709Sphk		    ap->len);
257115624Sphk		if (!(ap->flag & GCTL_PARAM_VALUEKERNEL)) {
258115624Sphk			printf(" =@ %p", ap->value);
259115624Sphk		} else if (ap->flag & GCTL_PARAM_ASCII) {
260115624Sphk			printf("\"%s\"", (char *)ap->kvalue);
261112511Sphk		} else if (ap->len > 0) {
262117150Sphk			for (j = 0; j < ap->len && j < 512; j++)
263115624Sphk				printf(" %02x", ((u_char *)ap->kvalue)[j]);
264112511Sphk		} else {
265115624Sphk			printf(" = %p", ap->kvalue);
266112511Sphk		}
267112511Sphk		printf("\n");
268112511Sphk	}
269112511Sphk}
270112511Sphk
271115624Sphkvoid
272115624Sphkgctl_set_param(struct gctl_req *req, const char *param, void const *ptr, int len)
273114670Sphk{
274115624Sphk	int i;
275114670Sphk	struct gctl_req_arg *ap;
276114670Sphk
277114670Sphk	for (i = 0; i < req->narg; i++) {
278114670Sphk		ap = &req->arg[i];
279114670Sphk		if (strcmp(param, ap->name))
280114670Sphk			continue;
281114670Sphk		if (!(ap->flag & GCTL_PARAM_WR)) {
282114670Sphk			gctl_error(req, "No write access %s argument", param);
283115624Sphk			return;
284114670Sphk		}
285115624Sphk		if (ap->len < len) {
286114670Sphk			gctl_error(req, "Wrong length %s argument", param);
287115624Sphk			return;
288114670Sphk		}
289115624Sphk		bcopy(ptr, ap->kvalue, len);
290115624Sphk		ap->flag |= GCTL_PARAM_CHANGED;
291115624Sphk		return;
292114670Sphk	}
293114670Sphk	gctl_error(req, "Missing %s argument", param);
294115624Sphk	return;
295114670Sphk}
296114670Sphk
297112709Sphkvoid *
298112709Sphkgctl_get_param(struct gctl_req *req, const char *param, int *len)
299112709Sphk{
300115624Sphk	int i;
301112709Sphk	void *p;
302112709Sphk	struct gctl_req_arg *ap;
303112709Sphk
304112709Sphk	for (i = 0; i < req->narg; i++) {
305112709Sphk		ap = &req->arg[i];
306112709Sphk		if (strcmp(param, ap->name))
307112709Sphk			continue;
308112709Sphk		if (!(ap->flag & GCTL_PARAM_RD))
309112709Sphk			continue;
310115624Sphk		p = ap->kvalue;
311112709Sphk		if (len != NULL)
312115624Sphk			*len = ap->len;
313112709Sphk		return (p);
314112709Sphk	}
315112709Sphk	return (NULL);
316112709Sphk}
317112709Sphk
318115624Sphkchar const *
319115624Sphkgctl_get_asciiparam(struct gctl_req *req, const char *param)
320115624Sphk{
321115624Sphk	int i;
322115624Sphk	char const *p;
323115624Sphk	struct gctl_req_arg *ap;
324115624Sphk
325115624Sphk	for (i = 0; i < req->narg; i++) {
326115624Sphk		ap = &req->arg[i];
327115624Sphk		if (strcmp(param, ap->name))
328115624Sphk			continue;
329115624Sphk		if (!(ap->flag & GCTL_PARAM_RD))
330115624Sphk			continue;
331115624Sphk		p = ap->kvalue;
332115624Sphk		if (ap->len < 1) {
333115624Sphk			gctl_error(req, "No length argument (%s)", param);
334115624Sphk			return (NULL);
335115624Sphk		}
336115624Sphk		if (p[ap->len - 1] != '\0') {
337115624Sphk			gctl_error(req, "Unterminated argument (%s)", param);
338115624Sphk			return (NULL);
339115624Sphk		}
340115624Sphk		return (p);
341115624Sphk	}
342115624Sphk	return (NULL);
343115624Sphk}
344115624Sphk
345113893Sphkvoid *
346113893Sphkgctl_get_paraml(struct gctl_req *req, const char *param, int len)
347113893Sphk{
348113893Sphk	int i;
349113893Sphk	void *p;
350113893Sphk
351113893Sphk	p = gctl_get_param(req, param, &i);
352113893Sphk	if (p == NULL)
353113893Sphk		gctl_error(req, "Missing %s argument", param);
354113893Sphk	else if (i != len) {
355113893Sphk		p = NULL;
356113893Sphk		gctl_error(req, "Wrong length %s argument", param);
357113893Sphk	}
358113893Sphk	return (p);
359113893Sphk}
360113893Sphk
361115624Sphkstruct g_class *
362115624Sphkgctl_get_class(struct gctl_req *req, char const *arg)
363112709Sphk{
364115624Sphk	char const *p;
365112709Sphk	struct g_class *cp;
366112709Sphk
367115624Sphk	p = gctl_get_asciiparam(req, arg);
368112709Sphk	if (p == NULL)
369112709Sphk		return (NULL);
370112709Sphk	LIST_FOREACH(cp, &g_classes, class) {
371115624Sphk		if (!strcmp(p, cp->name))
372112709Sphk			return (cp);
373112709Sphk	}
374112709Sphk	return (NULL);
375112709Sphk}
376112709Sphk
377115624Sphkstruct g_geom *
378115624Sphkgctl_get_geom(struct gctl_req *req, struct g_class *mpr, char const *arg)
379112709Sphk{
380115624Sphk	char const *p;
381112709Sphk	struct g_class *mp;
382112709Sphk	struct g_geom *gp;
383112709Sphk
384115624Sphk	p = gctl_get_asciiparam(req, arg);
385115958Sphk	if (p != NULL) {
386115958Sphk		LIST_FOREACH(mp, &g_classes, class) {
387115958Sphk			if (mpr != NULL && mpr != mp)
388115958Sphk				continue;
389115958Sphk			LIST_FOREACH(gp, &mp->geom, geom) {
390115958Sphk				if (!strcmp(p, gp->name))
391115958Sphk					return (gp);
392115958Sphk			}
393112709Sphk		}
394112709Sphk	}
395112709Sphk	gctl_error(req, "Geom not found");
396112709Sphk	return (NULL);
397112709Sphk}
398112709Sphk
399115624Sphkstruct g_provider *
400115624Sphkgctl_get_provider(struct gctl_req *req, char const *arg)
401112709Sphk{
402115624Sphk	char const *p;
403112709Sphk	struct g_provider *pp;
404112709Sphk
405115624Sphk	p = gctl_get_asciiparam(req, arg);
406112709Sphk	if (p == NULL)
407112709Sphk		return (NULL);
408115850Sphk	pp = g_provider_by_name(p);
409115850Sphk	if (pp != NULL)
410115850Sphk		return (pp);
411112709Sphk	gctl_error(req, "Provider not found");
412112709Sphk	return (NULL);
413112709Sphk}
414112709Sphk
415115624Sphkstatic void
416115624Sphkg_ctl_req(void *arg, int flag __unused)
417112709Sphk{
418112709Sphk	struct g_class *mp;
419115624Sphk	struct gctl_req *req;
420115624Sphk	char const *verb;
421112709Sphk
422112709Sphk	g_topology_assert();
423115624Sphk	req = arg;
424115624Sphk	mp = gctl_get_class(req, "class");
425115726Sphk	if (mp == NULL) {
426115726Sphk		gctl_error(req, "Class not found");
427115624Sphk		return;
428115726Sphk	}
429150304Smarcel	if (mp->ctlreq == NULL) {
430150304Smarcel		gctl_error(req, "Class takes no requests");
431150304Smarcel		return;
432150304Smarcel	}
433115624Sphk	verb = gctl_get_param(req, "verb", NULL);
434150304Smarcel	if (verb == NULL) {
435150304Smarcel		gctl_error(req, "Verb missing");
436150304Smarcel		return;
437150304Smarcel	}
438150304Smarcel	mp->ctlreq(req, mp, verb);
439112709Sphk	g_topology_assert();
440112709Sphk}
441112709Sphk
442112709Sphk
443113876Sphkstatic int
444130585Sphkg_ctl_ioctl_ctl(struct cdev *dev, u_long cmd, caddr_t data, int fflag, struct thread *td)
445112511Sphk{
446112709Sphk	struct gctl_req *req;
447136839Sphk	int nerror;
448112511Sphk
449112511Sphk	req = (void *)data;
450115624Sphk	req->nerror = 0;
451115624Sphk	req->serror = sbuf_new(NULL, NULL, 0, SBUF_AUTOEXTEND);
452112709Sphk	/* It is an error if we cannot return an error text */
453115624Sphk	if (req->lerror < 2)
454112511Sphk		return (EINVAL);
455112709Sphk	if (!useracc(req->error, req->lerror, VM_PROT_WRITE))
456112709Sphk		return (EINVAL);
457112709Sphk
458112709Sphk	/* Check the version */
459112709Sphk	if (req->version != GCTL_VERSION)
460112709Sphk		return (gctl_error(req,
461112709Sphk		    "kernel and libgeom version mismatch."));
462112709Sphk
463112709Sphk	/* Get things on board */
464115624Sphk	gctl_copyin(req);
465112709Sphk
466112876Sphk	if (g_debugflags & G_F_CTLDUMP)
467112876Sphk		gctl_dump(req);
468115624Sphk
469115624Sphk	if (!req->nerror) {
470115624Sphk		g_waitfor_event(g_ctl_req, req, M_WAITOK, NULL);
471115624Sphk		gctl_copyout(req);
472112709Sphk	}
473144789Spjd	if (sbuf_done(req->serror)) {
474144789Spjd		req->nerror = copyout(sbuf_data(req->serror), req->error,
475144789Spjd		    imin(req->lerror, sbuf_len(req->serror) + 1));
476144789Spjd	}
477115624Sphk
478136839Sphk	nerror = req->nerror;
479114531Sphk	gctl_free(req);
480136839Sphk	return (nerror);
481112511Sphk}
482112511Sphk
483112511Sphkstatic int
484130585Sphkg_ctl_ioctl(struct cdev *dev, u_long cmd, caddr_t data, int fflag, struct thread *td)
485105068Sphk{
486105068Sphk	int error;
487105068Sphk
488105068Sphk	switch(cmd) {
489112511Sphk	case GEOM_CTL:
490112511Sphk		error = g_ctl_ioctl_ctl(dev, cmd, data, fflag, td);
491112511Sphk		break;
492105068Sphk	default:
493115624Sphk		error = ENOIOCTL;
494105068Sphk		break;
495105068Sphk	}
496105068Sphk	return (error);
497105068Sphk
498105068Sphk}
499