geom_ctl.c revision 113893
1156230Smux/*-
2156230Smux * Copyright (c) 2002 Poul-Henning Kamp
3156230Smux * Copyright (c) 2002 Networks Associates Technology, Inc.
4156230Smux * All rights reserved.
5156230Smux *
6156230Smux * This software was developed for the FreeBSD Project by Poul-Henning Kamp
7156230Smux * and NAI Labs, the Security Research Division of Network Associates, Inc.
8156230Smux * under DARPA/SPAWAR contract N66001-01-C-8035 ("CBOSS"), as part of the
9156230Smux * DARPA CHATS research program.
10156230Smux *
11156230Smux * Redistribution and use in source and binary forms, with or without
12156230Smux * modification, are permitted provided that the following conditions
13156230Smux * are met:
14156230Smux * 1. Redistributions of source code must retain the above copyright
15156230Smux *    notice, this list of conditions and the following disclaimer.
16156230Smux * 2. Redistributions in binary form must reproduce the above copyright
17156230Smux *    notice, this list of conditions and the following disclaimer in the
18156230Smux *    documentation and/or other materials provided with the distribution.
19156230Smux * 3. The names of the authors may not be used to endorse or promote
20156230Smux *    products derived from this software without specific prior written
21156230Smux *    permission.
22156230Smux *
23156230Smux * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
24156230Smux * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
25156230Smux * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
26156230Smux * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
27156230Smux * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
28156230Smux * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
29156230Smux * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
30156230Smux * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
31156230Smux * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
32156230Smux * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
33156230Smux * SUCH DAMAGE.
34156230Smux *
35156230Smux * $FreeBSD: head/sys/geom/geom_ctl.c 113893 2003-04-23 08:03:47Z phk $
36156230Smux */
37156230Smux
38156230Smux#include "opt_geom.h"
39156230Smux
40156230Smux#include <sys/param.h>
41156230Smux#include <sys/systm.h>
42156230Smux#include <sys/kernel.h>
43156230Smux#include <sys/sysctl.h>
44156230Smux#include <sys/bio.h>
45156230Smux#include <sys/conf.h>
46156230Smux#include <sys/disk.h>
47156230Smux#include <sys/malloc.h>
48156230Smux#include <sys/sysctl.h>
49156230Smux#include <sys/sbuf.h>
50156230Smux
51156230Smux#include <sys/lock.h>
52156230Smux#include <sys/mutex.h>
53156230Smux
54156230Smux#include <vm/vm.h>
55156230Smux#include <vm/vm_extern.h>
56156230Smux
57156230Smux#include <geom/geom.h>
58156230Smux#include <geom/geom_int.h>
59156230Smux#define GCTL_TABLE 1
60156230Smux#include <geom/geom_ctl.h>
61156230Smux#include <geom/geom_ext.h>
62156230Smux
63156230Smux#include <machine/stdarg.h>
64156230Smux
65156230Smuxstatic d_ioctl_t g_ctl_ioctl;
66156230Smux
67156230Smuxstatic struct cdevsw g_ctl_cdevsw = {
68156230Smux	.d_open =	nullopen,
69156230Smux	.d_close =	nullclose,
70156230Smux	.d_ioctl =	g_ctl_ioctl,
71156230Smux	.d_name =	"g_ctl",
72156230Smux};
73156230Smux
74156230Smuxvoid
75156230Smuxg_ctl_init(void)
76156230Smux{
77156230Smux
78156230Smux	make_dev(&g_ctl_cdevsw, 0,
79156230Smux	    UID_ROOT, GID_OPERATOR, 0640, PATH_GEOM_CTL);
80156230Smux	KASSERT(GCTL_PARAM_RD == VM_PROT_READ,
81156230Smux		("GCTL_PARAM_RD != VM_PROT_READ"));
82156230Smux	KASSERT(GCTL_PARAM_WR == VM_PROT_WRITE,
83156230Smux		("GCTL_PARAM_WR != VM_PROT_WRITE"));
84156230Smux}
85156230Smux
86156230Smux/*
87156230Smux * Report an error back to the user in ascii format.  Return whatever copyout
88156230Smux * returned, or EINVAL if it succeeded.
89156230Smux * XXX: should not be static.
90156230Smux * XXX: should take printf like args.
91156230Smux */
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_dump(struct gctl_req *req)
200{
201	u_int i;
202	int j, error;
203	struct gctl_req_arg *ap;
204	void *p;
205
206
207	printf("Dump of gctl %s request at %p:\n", req->reqt->name, req);
208	if (req->lerror > 0) {
209		p = geom_alloc_copyin(req, req->error, req->lerror, &error);
210		if (p != NULL) {
211			((char *)p)[req->lerror - 1] = '\0';
212			printf("  error:\t\"%s\"\n", (char *)p);
213			g_free(p);
214		}
215	}
216	for (i = 0; i < req->narg; i++) {
217		ap = &req->arg[i];
218		printf("  param:\t\"%s\"", ap->name);
219		printf(" [%s%s%d] = ",
220		    ap->flag & GCTL_PARAM_RD ? "R" : "",
221		    ap->flag & GCTL_PARAM_WR ? "W" : "",
222		    ap->len);
223		if (ap->flag & GCTL_PARAM_ASCII) {
224			p = geom_alloc_copyin(req, ap->value, ap->len, &error);
225			if (p != NULL) {
226				((char *)p)[ap->len - 1] = '\0';
227				printf("\"%s\"", (char *)p);
228			}
229			g_free(p);
230		} else if (ap->len > 0) {
231			p = geom_alloc_copyin(req, ap->value, ap->len, &error);
232			for (j = 0; j < ap->len; j++)
233				printf(" %02x", ((u_char *)p)[j]);
234			g_free(p);
235		} else {
236			printf(" = %p", ap->value);
237		}
238		printf("\n");
239	}
240}
241
242void *
243gctl_get_param(struct gctl_req *req, const char *param, int *len)
244{
245	int i, error, j;
246	void *p;
247	struct gctl_req_arg *ap;
248
249	for (i = 0; i < req->narg; i++) {
250		ap = &req->arg[i];
251		if (strcmp(param, ap->name))
252			continue;
253		if (!(ap->flag & GCTL_PARAM_RD))
254			continue;
255		if (ap->len > 0)
256			j = ap->len;
257		else
258			j = 0;
259		if (j != 0)
260			p = geom_alloc_copyin(req, ap->value, j, &error);
261			/* XXX: should not fail, tested prviously */
262		else
263			p = ap->value;
264		if (len != NULL)
265			*len = j;
266		return (p);
267	}
268	return (NULL);
269}
270
271void *
272gctl_get_paraml(struct gctl_req *req, const char *param, int len)
273{
274	int i;
275	void *p;
276
277	p = gctl_get_param(req, param, &i);
278	if (p == NULL)
279		gctl_error(req, "Missing %s argument", param);
280	else if (i != len) {
281		g_free(p);
282		p = NULL;
283		gctl_error(req, "Wrong length %s argument", param);
284	}
285	return (p);
286}
287
288static struct g_class*
289gctl_get_class(struct gctl_req *req)
290{
291	char *p;
292	int len;
293	struct g_class *cp;
294
295	p = gctl_get_param(req, "class", &len);
296	if (p == NULL)
297		return (NULL);
298	if (p[len - 1] != '\0') {
299		gctl_error(req, "Unterminated class name");
300		g_free(p);
301		return (NULL);
302	}
303	LIST_FOREACH(cp, &g_classes, class) {
304		if (!strcmp(p, cp->name)) {
305			g_free(p);
306			return (cp);
307		}
308	}
309	gctl_error(req, "Class not found");
310	return (NULL);
311}
312
313static struct g_geom*
314gctl_get_geom(struct gctl_req *req, struct g_class *mpr)
315{
316	char *p;
317	int len;
318	struct g_class *mp;
319	struct g_geom *gp;
320
321	p = gctl_get_param(req, "geom", &len);
322	if (p == NULL)
323		return (NULL);
324	if (p[len - 1] != '\0') {
325		gctl_error(req, "Unterminated provider name");
326		g_free(p);
327		return (NULL);
328	}
329	LIST_FOREACH(mp, &g_classes, class) {
330		if (mpr != NULL && mpr != mp)
331			continue;
332		LIST_FOREACH(gp, &mp->geom, geom) {
333			if (!strcmp(p, gp->name)) {
334				g_free(p);
335				return (gp);
336			}
337		}
338	}
339	gctl_error(req, "Geom not found");
340	return (NULL);
341}
342
343static struct g_provider*
344gctl_get_provider(struct gctl_req *req)
345{
346	char *p;
347	int len;
348	struct g_class *cp;
349	struct g_geom *gp;
350	struct g_provider *pp;
351
352	p = gctl_get_param(req, "provider", &len);
353	if (p == NULL)
354		return (NULL);
355	if (p[len - 1] != '\0') {
356		gctl_error(req, "Unterminated provider name");
357		g_free(p);
358		return (NULL);
359	}
360	LIST_FOREACH(cp, &g_classes, class) {
361		LIST_FOREACH(gp, &cp->geom, geom) {
362			LIST_FOREACH(pp, &gp->provider, provider) {
363				if (!strcmp(p, pp->name)) {
364					g_free(p);
365					return (pp);
366				}
367			}
368		}
369	}
370	gctl_error(req, "Provider not found");
371	return (NULL);
372}
373
374static int
375gctl_create_geom(struct gctl_req *req)
376{
377	struct g_class *mp;
378	struct g_provider *pp;
379	int error;
380
381	g_topology_assert();
382	mp = gctl_get_class(req);
383	if (mp == NULL)
384		return (gctl_error(req, "Class not found"));
385	if (mp->create_geom == NULL)
386		return (gctl_error(req, "Class has no create_geom method"));
387	pp = gctl_get_provider(req);
388	error = mp->create_geom(req, mp, pp);
389	g_topology_assert();
390	return (error);
391}
392
393static int
394gctl_destroy_geom(struct gctl_req *req)
395{
396	struct g_class *mp;
397	struct g_geom *gp;
398	int error;
399
400	g_topology_assert();
401	mp = gctl_get_class(req);
402	if (mp == NULL)
403		return (gctl_error(req, "Class not found"));
404	if (mp->destroy_geom == NULL)
405		return (gctl_error(req, "Class has no destroy_geom method"));
406	gp = gctl_get_geom(req, mp);
407	if (gp == NULL)
408		return (gctl_error(req, "Geom not specified"));
409	if (gp->class != mp)
410		return (gctl_error(req, "Geom not of specificed class"));
411	error = mp->destroy_geom(req, mp, gp);
412	g_topology_assert();
413	return (error);
414}
415
416static int
417gctl_config_geom(struct gctl_req *req)
418{
419	struct g_class *mp;
420	struct g_geom *gp;
421	char *verb;
422	int error, vlen;
423
424	g_topology_assert();
425	mp = gctl_get_class(req);
426	if (mp == NULL)
427		return (gctl_error(req, "Class not found"));
428	if (mp->config_geom == NULL)
429		return (gctl_error(req, "Class has no config_geom method"));
430	gp = gctl_get_geom(req, mp);
431	if (gp == NULL)
432		return (gctl_error(req, "Geom not specified"));
433	if (gp->class != mp)
434		return (gctl_error(req, "Geom not of specificed class"));
435	verb = gctl_get_param(req, "verb", &vlen);
436	if (verb == NULL)
437		return (gctl_error(req, "Missing verb parameter"));
438	if (vlen < 2) {
439		g_free(verb);
440		return (gctl_error(req, "Too short verb parameter"));
441	}
442	if (verb[vlen - 1] != '\0') {
443		g_free(verb);
444		return (gctl_error(req, "Unterminated verb parameter"));
445	}
446	error = mp->config_geom(req, gp, verb);
447	g_free(verb);
448	g_topology_assert();
449	return (error);
450}
451
452/*
453 * Handle ioctl from libgeom::geom_ctl.c
454 */
455static int
456g_ctl_ioctl_ctl(dev_t dev, u_long cmd, caddr_t data, int fflag, struct thread *td)
457{
458	int error;
459	int i;
460	struct gctl_req *req;
461
462	req = (void *)data;
463	/* It is an error if we cannot return an error text */
464	if (req->lerror < 1)
465		return (EINVAL);
466	if (!useracc(req->error, req->lerror, VM_PROT_WRITE))
467		return (EINVAL);
468
469	/* Check the version */
470	if (req->version != GCTL_VERSION)
471		return (gctl_error(req,
472		    "kernel and libgeom version mismatch."));
473
474	/* Check the request type */
475	for (i = 0; gcrt[i].request != GCTL_INVALID_REQUEST; i++)
476		if (gcrt[i].request == req->request)
477			break;
478	if (gcrt[i].request == GCTL_INVALID_REQUEST)
479		return (gctl_error(req, "invalid request"));
480	req->reqt = &gcrt[i];
481
482	/* Get things on board */
483	error = gctl_copyin(req);
484	if (error)
485		return (error);
486
487	if (g_debugflags & G_F_CTLDUMP)
488		gctl_dump(req);
489	g_topology_lock();
490	switch (req->request) {
491	case GCTL_CREATE_GEOM:
492		error = gctl_create_geom(req);
493		break;
494	case GCTL_DESTROY_GEOM:
495		error = gctl_destroy_geom(req);
496		break;
497	case GCTL_CONFIG_GEOM:
498		error = gctl_config_geom(req);
499		break;
500	default:
501		error = gctl_error(req, "XXX: TBD");
502		break;
503	}
504	g_topology_unlock();
505	return (error);
506}
507
508static int
509g_ctl_ioctl(dev_t dev, u_long cmd, caddr_t data, int fflag, struct thread *td)
510{
511	int error;
512
513	switch(cmd) {
514	case GEOM_CTL:
515		DROP_GIANT();
516		error = g_ctl_ioctl_ctl(dev, cmd, data, fflag, td);
517		PICKUP_GIANT();
518		break;
519	default:
520		error = ENOTTY;
521		break;
522	}
523	return (error);
524
525}
526