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