geom_ctl.c revision 114670
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 114670 2003-05-04 19:24:34Z 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
254int
255gctl_set_param(struct gctl_req *req, const char *param, void *ptr, int len)
256{
257	int i, error;
258	struct gctl_req_arg *ap;
259
260	for (i = 0; i < req->narg; i++) {
261		ap = &req->arg[i];
262		if (strcmp(param, ap->name))
263			continue;
264		if (!(ap->flag & GCTL_PARAM_WR)) {
265			gctl_error(req, "No write access %s argument", param);
266			return (EINVAL);
267		}
268		if (ap->len != len) {
269			gctl_error(req, "Wrong length %s argument", param);
270			return (EINVAL);
271		}
272		error = copyout(ptr, ap->value, len);
273		return (error);
274	}
275	gctl_error(req, "Missing %s argument", param);
276	return (EINVAL);
277}
278
279void *
280gctl_get_param(struct gctl_req *req, const char *param, int *len)
281{
282	int i, error, j;
283	void *p;
284	struct gctl_req_arg *ap;
285
286	for (i = 0; i < req->narg; i++) {
287		ap = &req->arg[i];
288		if (strcmp(param, ap->name))
289			continue;
290		if (!(ap->flag & GCTL_PARAM_RD))
291			continue;
292		if (ap->len > 0)
293			j = ap->len;
294		else
295			j = 0;
296		if (j != 0)
297			p = geom_alloc_copyin(req, ap->value, j, &error);
298			/* XXX: should not fail, tested prviously */
299		else
300			p = ap->value;
301		if (len != NULL)
302			*len = j;
303		return (p);
304	}
305	return (NULL);
306}
307
308void *
309gctl_get_paraml(struct gctl_req *req, const char *param, int len)
310{
311	int i;
312	void *p;
313
314	p = gctl_get_param(req, param, &i);
315	if (p == NULL)
316		gctl_error(req, "Missing %s argument", param);
317	else if (i != len) {
318		g_free(p);
319		p = NULL;
320		gctl_error(req, "Wrong length %s argument", param);
321	}
322	return (p);
323}
324
325static struct g_class*
326gctl_get_class(struct gctl_req *req)
327{
328	char *p;
329	int len;
330	struct g_class *cp;
331
332	p = gctl_get_param(req, "class", &len);
333	if (p == NULL)
334		return (NULL);
335	if (p[len - 1] != '\0') {
336		gctl_error(req, "Unterminated class name");
337		g_free(p);
338		return (NULL);
339	}
340	LIST_FOREACH(cp, &g_classes, class) {
341		if (!strcmp(p, cp->name)) {
342			g_free(p);
343			return (cp);
344		}
345	}
346	g_free(p);
347	gctl_error(req, "Class not found");
348	return (NULL);
349}
350
351static struct g_geom*
352gctl_get_geom(struct gctl_req *req, struct g_class *mpr)
353{
354	char *p;
355	int len;
356	struct g_class *mp;
357	struct g_geom *gp;
358
359	p = gctl_get_param(req, "geom", &len);
360	if (p == NULL)
361		return (NULL);
362	if (p[len - 1] != '\0') {
363		gctl_error(req, "Unterminated provider name");
364		g_free(p);
365		return (NULL);
366	}
367	LIST_FOREACH(mp, &g_classes, class) {
368		if (mpr != NULL && mpr != mp)
369			continue;
370		LIST_FOREACH(gp, &mp->geom, geom) {
371			if (!strcmp(p, gp->name)) {
372				g_free(p);
373				return (gp);
374			}
375		}
376	}
377	gctl_error(req, "Geom not found");
378	g_free(p);
379	return (NULL);
380}
381
382static struct g_provider*
383gctl_get_provider(struct gctl_req *req)
384{
385	char *p;
386	int len;
387	struct g_class *cp;
388	struct g_geom *gp;
389	struct g_provider *pp;
390
391	p = gctl_get_param(req, "provider", &len);
392	if (p == NULL)
393		return (NULL);
394	if (p[len - 1] != '\0') {
395		gctl_error(req, "Unterminated provider name");
396		g_free(p);
397		return (NULL);
398	}
399	LIST_FOREACH(cp, &g_classes, class) {
400		LIST_FOREACH(gp, &cp->geom, geom) {
401			LIST_FOREACH(pp, &gp->provider, provider) {
402				if (!strcmp(p, pp->name)) {
403					g_free(p);
404					return (pp);
405				}
406			}
407		}
408	}
409	gctl_error(req, "Provider not found");
410	g_free(p);
411	return (NULL);
412}
413
414static int
415gctl_create_geom(struct gctl_req *req)
416{
417	struct g_class *mp;
418	struct g_provider *pp;
419	int error;
420
421	g_topology_assert();
422	mp = gctl_get_class(req);
423	if (mp == NULL)
424		return (gctl_error(req, "Class not found"));
425	if (mp->create_geom == NULL)
426		return (gctl_error(req, "Class has no create_geom method"));
427	pp = gctl_get_provider(req);
428	error = mp->create_geom(req, mp, pp);
429	g_topology_assert();
430	return (error);
431}
432
433static int
434gctl_destroy_geom(struct gctl_req *req)
435{
436	struct g_class *mp;
437	struct g_geom *gp;
438	int error;
439
440	g_topology_assert();
441	mp = gctl_get_class(req);
442	if (mp == NULL)
443		return (gctl_error(req, "Class not found"));
444	if (mp->destroy_geom == NULL)
445		return (gctl_error(req, "Class has no destroy_geom method"));
446	gp = gctl_get_geom(req, mp);
447	if (gp == NULL)
448		return (gctl_error(req, "Geom not specified"));
449	if (gp->class != mp)
450		return (gctl_error(req, "Geom not of specificed class"));
451	error = mp->destroy_geom(req, mp, gp);
452	g_topology_assert();
453	return (error);
454}
455
456static int
457gctl_config_geom(struct gctl_req *req)
458{
459	struct g_class *mp;
460	struct g_geom *gp;
461	char *verb;
462	int error, vlen;
463
464	g_topology_assert();
465	mp = gctl_get_class(req);
466	if (mp == NULL)
467		return (gctl_error(req, "Class not found"));
468	if (mp->config_geom == NULL)
469		return (gctl_error(req, "Class has no config_geom method"));
470	gp = gctl_get_geom(req, mp);
471	if (gp == NULL)
472		return (gctl_error(req, "Geom not specified"));
473	if (gp->class != mp)
474		return (gctl_error(req, "Geom not of specificed class"));
475	verb = gctl_get_param(req, "verb", &vlen);
476	if (verb == NULL)
477		return (gctl_error(req, "Missing verb parameter"));
478	if (vlen < 2) {
479		g_free(verb);
480		return (gctl_error(req, "Too short verb parameter"));
481	}
482	if (verb[vlen - 1] != '\0') {
483		g_free(verb);
484		return (gctl_error(req, "Unterminated verb parameter"));
485	}
486	error = mp->config_geom(req, gp, verb);
487	g_free(verb);
488	g_topology_assert();
489	return (error);
490}
491
492/*
493 * Handle ioctl from libgeom::geom_ctl.c
494 */
495static int
496g_ctl_ioctl_ctl(dev_t dev, u_long cmd, caddr_t data, int fflag, struct thread *td)
497{
498	int error;
499	int i;
500	struct gctl_req *req;
501
502	req = (void *)data;
503	/* It is an error if we cannot return an error text */
504	if (req->lerror < 1)
505		return (EINVAL);
506	if (!useracc(req->error, req->lerror, VM_PROT_WRITE))
507		return (EINVAL);
508
509	/* Check the version */
510	if (req->version != GCTL_VERSION)
511		return (gctl_error(req,
512		    "kernel and libgeom version mismatch."));
513
514	/* Check the request type */
515	for (i = 0; gcrt[i].request != GCTL_INVALID_REQUEST; i++)
516		if (gcrt[i].request == req->request)
517			break;
518	if (gcrt[i].request == GCTL_INVALID_REQUEST)
519		return (gctl_error(req, "invalid request"));
520	req->reqt = &gcrt[i];
521
522	/* Get things on board */
523	error = gctl_copyin(req);
524	if (error)
525		return (error);
526
527	if (g_debugflags & G_F_CTLDUMP)
528		gctl_dump(req);
529	g_topology_lock();
530	switch (req->request) {
531	case GCTL_CREATE_GEOM:
532		error = gctl_create_geom(req);
533		break;
534	case GCTL_DESTROY_GEOM:
535		error = gctl_destroy_geom(req);
536		break;
537	case GCTL_CONFIG_GEOM:
538		error = gctl_config_geom(req);
539		break;
540	default:
541		error = gctl_error(req, "XXX: TBD");
542		break;
543	}
544	gctl_free(req);
545	g_topology_unlock();
546	return (error);
547}
548
549static int
550g_ctl_ioctl(dev_t dev, u_long cmd, caddr_t data, int fflag, struct thread *td)
551{
552	int error;
553
554	switch(cmd) {
555	case GEOM_CTL:
556		DROP_GIANT();
557		error = g_ctl_ioctl_ctl(dev, cmd, data, fflag, td);
558		PICKUP_GIANT();
559		break;
560	default:
561		error = ENOTTY;
562		break;
563	}
564	return (error);
565
566}
567