geom_ctl.c revision 114531
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 * $FreeBSD: head/sys/geom/geom_ctl.c 114531 2003-05-02 12:49:41Z phk $
36 */
37
38#include "opt_geom.h"
39
40#include <sys/param.h>
41#include <sys/systm.h>
42#include <sys/kernel.h>
43#include <sys/sysctl.h>
44#include <sys/bio.h>
45#include <sys/conf.h>
46#include <sys/disk.h>
47#include <sys/malloc.h>
48#include <sys/sysctl.h>
49#include <sys/sbuf.h>
50
51#include <sys/lock.h>
52#include <sys/mutex.h>
53
54#include <vm/vm.h>
55#include <vm/vm_extern.h>
56
57#include <geom/geom.h>
58#include <geom/geom_int.h>
59#define GCTL_TABLE 1
60#include <geom/geom_ctl.h>
61#include <geom/geom_ext.h>
62
63#include <machine/stdarg.h>
64
65static d_ioctl_t g_ctl_ioctl;
66
67static struct cdevsw g_ctl_cdevsw = {
68	.d_open =	nullopen,
69	.d_close =	nullclose,
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	int error;
96	va_list ap;
97	struct sbuf *sb;
98
99	sb = sbuf_new(NULL, NULL, 0, SBUF_AUTOEXTEND);
100	if (sb == NULL) {
101		error = copyout(fmt, req->error,
102			    imin(req->lerror, strlen(fmt) + 1));
103	} else {
104		va_start(ap, fmt);
105		sbuf_vprintf(sb, fmt, ap);
106		sbuf_finish(sb);
107		if (g_debugflags & G_F_CTLDUMP)
108			printf("gctl %p error \"%s\"\n", req, sbuf_data(sb));
109		error = copyout(sbuf_data(sb), req->error,
110		    imin(req->lerror, sbuf_len(sb) + 1));
111	}
112	if (!error)
113		error = EINVAL;
114	sbuf_delete(sb);
115	return (error);
116}
117
118/*
119 * Allocate space and copyin() something.
120 * XXX: this should really be a standard function in the kernel.
121 */
122static void *
123geom_alloc_copyin(struct gctl_req *req, void *uaddr, size_t len, int *errp)
124{
125	int error;
126	void *ptr;
127
128	ptr = g_malloc(len, M_WAITOK);
129	if (ptr == NULL)
130		error = ENOMEM;
131	else
132		error = copyin(uaddr, ptr, len);
133	if (!error)
134		return (ptr);
135	gctl_error(req, "no access to argument");
136	*errp = error;
137	if (ptr != NULL)
138		g_free(ptr);
139	return (NULL);
140}
141
142
143/*
144 * XXX: This function is a nightmare.  It walks through the request and
145 * XXX: makes sure that the various bits and pieces are there and copies
146 * XXX: some of them into kernel memory to make things easier.
147 * XXX: I really wish we had a standard marshalling layer somewhere.
148 */
149
150static int
151gctl_copyin(struct gctl_req *req)
152{
153	int error, i, j;
154	struct gctl_req_arg *ap;
155	char *p;
156
157	error = 0;
158	ap = geom_alloc_copyin(req, req->arg, req->narg * sizeof(*ap), &error);
159	if (ap == NULL) {
160		gctl_error(req, "copyin() of arguments failed");
161		return (error);
162	}
163
164	for (i = 0; !error && i < req->narg; i++) {
165		if (ap[i].len > 0 &&
166		    !useracc(ap[i].value, ap[i].len,
167		    ap[i].flag & GCTL_PARAM_RW))
168			error = gctl_error(req, "no access to param data");
169		if (error)
170			break;
171		p = NULL;
172		if (ap[i].nlen < 1 || ap[i].nlen > SPECNAMELEN) {
173			error = gctl_error(req, "wrong param name length");
174			break;
175		}
176		p = geom_alloc_copyin(req, ap[i].name, ap[i].nlen, &error);
177		if (p == NULL)
178			break;
179		if (p[ap[i].nlen - 1] != '\0') {
180			error = gctl_error(req, "unterminated param name");
181			g_free(p);
182			break;
183		}
184		ap[i].name = p;
185		ap[i].nlen = 0;
186	}
187	if (!error) {
188		req->arg = ap;
189		return (0);
190	}
191	for (j = 0; j < i; j++)
192		if (ap[j].nlen == 0 && ap[j].name != NULL)
193			g_free(ap[j].name);
194	g_free(ap);
195	return (error);
196}
197
198static void
199gctl_free(struct gctl_req *req)
200{
201	int i;
202
203	for (i = 0; i < req->narg; i++) {
204		if (req->arg[i].nlen == 0 && req->arg[i].name != NULL)
205			g_free(req->arg[i].name);
206	}
207	g_free(req->arg);
208}
209
210static void
211gctl_dump(struct gctl_req *req)
212{
213	u_int i;
214	int j, error;
215	struct gctl_req_arg *ap;
216	void *p;
217
218
219	printf("Dump of gctl %s request at %p:\n", req->reqt->name, req);
220	if (req->lerror > 0) {
221		p = geom_alloc_copyin(req, req->error, req->lerror, &error);
222		if (p != NULL) {
223			((char *)p)[req->lerror - 1] = '\0';
224			printf("  error:\t\"%s\"\n", (char *)p);
225			g_free(p);
226		}
227	}
228	for (i = 0; i < req->narg; i++) {
229		ap = &req->arg[i];
230		printf("  param:\t\"%s\"", ap->name);
231		printf(" [%s%s%d] = ",
232		    ap->flag & GCTL_PARAM_RD ? "R" : "",
233		    ap->flag & GCTL_PARAM_WR ? "W" : "",
234		    ap->len);
235		if (ap->flag & GCTL_PARAM_ASCII) {
236			p = geom_alloc_copyin(req, ap->value, ap->len, &error);
237			if (p != NULL) {
238				((char *)p)[ap->len - 1] = '\0';
239				printf("\"%s\"", (char *)p);
240			}
241			g_free(p);
242		} else if (ap->len > 0) {
243			p = geom_alloc_copyin(req, ap->value, ap->len, &error);
244			for (j = 0; j < ap->len; j++)
245				printf(" %02x", ((u_char *)p)[j]);
246			g_free(p);
247		} else {
248			printf(" = %p", ap->value);
249		}
250		printf("\n");
251	}
252}
253
254void *
255gctl_get_param(struct gctl_req *req, const char *param, int *len)
256{
257	int i, error, j;
258	void *p;
259	struct gctl_req_arg *ap;
260
261	for (i = 0; i < req->narg; i++) {
262		ap = &req->arg[i];
263		if (strcmp(param, ap->name))
264			continue;
265		if (!(ap->flag & GCTL_PARAM_RD))
266			continue;
267		if (ap->len > 0)
268			j = ap->len;
269		else
270			j = 0;
271		if (j != 0)
272			p = geom_alloc_copyin(req, ap->value, j, &error);
273			/* XXX: should not fail, tested prviously */
274		else
275			p = ap->value;
276		if (len != NULL)
277			*len = j;
278		return (p);
279	}
280	return (NULL);
281}
282
283void *
284gctl_get_paraml(struct gctl_req *req, const char *param, int len)
285{
286	int i;
287	void *p;
288
289	p = gctl_get_param(req, param, &i);
290	if (p == NULL)
291		gctl_error(req, "Missing %s argument", param);
292	else if (i != len) {
293		g_free(p);
294		p = NULL;
295		gctl_error(req, "Wrong length %s argument", param);
296	}
297	return (p);
298}
299
300static struct g_class*
301gctl_get_class(struct gctl_req *req)
302{
303	char *p;
304	int len;
305	struct g_class *cp;
306
307	p = gctl_get_param(req, "class", &len);
308	if (p == NULL)
309		return (NULL);
310	if (p[len - 1] != '\0') {
311		gctl_error(req, "Unterminated class name");
312		g_free(p);
313		return (NULL);
314	}
315	LIST_FOREACH(cp, &g_classes, class) {
316		if (!strcmp(p, cp->name)) {
317			g_free(p);
318			return (cp);
319		}
320	}
321	g_free(p);
322	gctl_error(req, "Class not found");
323	return (NULL);
324}
325
326static struct g_geom*
327gctl_get_geom(struct gctl_req *req, struct g_class *mpr)
328{
329	char *p;
330	int len;
331	struct g_class *mp;
332	struct g_geom *gp;
333
334	p = gctl_get_param(req, "geom", &len);
335	if (p == NULL)
336		return (NULL);
337	if (p[len - 1] != '\0') {
338		gctl_error(req, "Unterminated provider name");
339		g_free(p);
340		return (NULL);
341	}
342	LIST_FOREACH(mp, &g_classes, class) {
343		if (mpr != NULL && mpr != mp)
344			continue;
345		LIST_FOREACH(gp, &mp->geom, geom) {
346			if (!strcmp(p, gp->name)) {
347				g_free(p);
348				return (gp);
349			}
350		}
351	}
352	gctl_error(req, "Geom not found");
353	g_free(p);
354	return (NULL);
355}
356
357static struct g_provider*
358gctl_get_provider(struct gctl_req *req)
359{
360	char *p;
361	int len;
362	struct g_class *cp;
363	struct g_geom *gp;
364	struct g_provider *pp;
365
366	p = gctl_get_param(req, "provider", &len);
367	if (p == NULL)
368		return (NULL);
369	if (p[len - 1] != '\0') {
370		gctl_error(req, "Unterminated provider name");
371		g_free(p);
372		return (NULL);
373	}
374	LIST_FOREACH(cp, &g_classes, class) {
375		LIST_FOREACH(gp, &cp->geom, geom) {
376			LIST_FOREACH(pp, &gp->provider, provider) {
377				if (!strcmp(p, pp->name)) {
378					g_free(p);
379					return (pp);
380				}
381			}
382		}
383	}
384	gctl_error(req, "Provider not found");
385	g_free(p);
386	return (NULL);
387}
388
389static int
390gctl_create_geom(struct gctl_req *req)
391{
392	struct g_class *mp;
393	struct g_provider *pp;
394	int error;
395
396	g_topology_assert();
397	mp = gctl_get_class(req);
398	if (mp == NULL)
399		return (gctl_error(req, "Class not found"));
400	if (mp->create_geom == NULL)
401		return (gctl_error(req, "Class has no create_geom method"));
402	pp = gctl_get_provider(req);
403	error = mp->create_geom(req, mp, pp);
404	g_topology_assert();
405	return (error);
406}
407
408static int
409gctl_destroy_geom(struct gctl_req *req)
410{
411	struct g_class *mp;
412	struct g_geom *gp;
413	int error;
414
415	g_topology_assert();
416	mp = gctl_get_class(req);
417	if (mp == NULL)
418		return (gctl_error(req, "Class not found"));
419	if (mp->destroy_geom == NULL)
420		return (gctl_error(req, "Class has no destroy_geom method"));
421	gp = gctl_get_geom(req, mp);
422	if (gp == NULL)
423		return (gctl_error(req, "Geom not specified"));
424	if (gp->class != mp)
425		return (gctl_error(req, "Geom not of specificed class"));
426	error = mp->destroy_geom(req, mp, gp);
427	g_topology_assert();
428	return (error);
429}
430
431static int
432gctl_config_geom(struct gctl_req *req)
433{
434	struct g_class *mp;
435	struct g_geom *gp;
436	char *verb;
437	int error, vlen;
438
439	g_topology_assert();
440	mp = gctl_get_class(req);
441	if (mp == NULL)
442		return (gctl_error(req, "Class not found"));
443	if (mp->config_geom == NULL)
444		return (gctl_error(req, "Class has no config_geom method"));
445	gp = gctl_get_geom(req, mp);
446	if (gp == NULL)
447		return (gctl_error(req, "Geom not specified"));
448	if (gp->class != mp)
449		return (gctl_error(req, "Geom not of specificed class"));
450	verb = gctl_get_param(req, "verb", &vlen);
451	if (verb == NULL)
452		return (gctl_error(req, "Missing verb parameter"));
453	if (vlen < 2) {
454		g_free(verb);
455		return (gctl_error(req, "Too short verb parameter"));
456	}
457	if (verb[vlen - 1] != '\0') {
458		g_free(verb);
459		return (gctl_error(req, "Unterminated verb parameter"));
460	}
461	error = mp->config_geom(req, gp, verb);
462	g_free(verb);
463	g_topology_assert();
464	return (error);
465}
466
467/*
468 * Handle ioctl from libgeom::geom_ctl.c
469 */
470static int
471g_ctl_ioctl_ctl(dev_t dev, u_long cmd, caddr_t data, int fflag, struct thread *td)
472{
473	int error;
474	int i;
475	struct gctl_req *req;
476
477	req = (void *)data;
478	/* It is an error if we cannot return an error text */
479	if (req->lerror < 1)
480		return (EINVAL);
481	if (!useracc(req->error, req->lerror, VM_PROT_WRITE))
482		return (EINVAL);
483
484	/* Check the version */
485	if (req->version != GCTL_VERSION)
486		return (gctl_error(req,
487		    "kernel and libgeom version mismatch."));
488
489	/* Check the request type */
490	for (i = 0; gcrt[i].request != GCTL_INVALID_REQUEST; i++)
491		if (gcrt[i].request == req->request)
492			break;
493	if (gcrt[i].request == GCTL_INVALID_REQUEST)
494		return (gctl_error(req, "invalid request"));
495	req->reqt = &gcrt[i];
496
497	/* Get things on board */
498	error = gctl_copyin(req);
499	if (error)
500		return (error);
501
502	if (g_debugflags & G_F_CTLDUMP)
503		gctl_dump(req);
504	g_topology_lock();
505	switch (req->request) {
506	case GCTL_CREATE_GEOM:
507		error = gctl_create_geom(req);
508		break;
509	case GCTL_DESTROY_GEOM:
510		error = gctl_destroy_geom(req);
511		break;
512	case GCTL_CONFIG_GEOM:
513		error = gctl_config_geom(req);
514		break;
515	default:
516		error = gctl_error(req, "XXX: TBD");
517		break;
518	}
519	gctl_free(req);
520	g_topology_unlock();
521	return (error);
522}
523
524static int
525g_ctl_ioctl(dev_t dev, u_long cmd, caddr_t data, int fflag, struct thread *td)
526{
527	int error;
528
529	switch(cmd) {
530	case GEOM_CTL:
531		DROP_GIANT();
532		error = g_ctl_ioctl_ctl(dev, cmd, data, fflag, td);
533		PICKUP_GIANT();
534		break;
535	default:
536		error = ENOTTY;
537		break;
538	}
539	return (error);
540
541}
542