geom_ctl.c revision 112876
1156952Sume/*-
2156952Sume * Copyright (c) 2002 Poul-Henning Kamp
3156952Sume * Copyright (c) 2002 Networks Associates Technology, Inc.
4156952Sume * All rights reserved.
5156952Sume *
6156952Sume * This software was developed for the FreeBSD Project by Poul-Henning Kamp
7156952Sume * and NAI Labs, the Security Research Division of Network Associates, Inc.
8156952Sume * under DARPA/SPAWAR contract N66001-01-C-8035 ("CBOSS"), as part of the
9156952Sume * DARPA CHATS research program.
10156952Sume *
11156952Sume * Redistribution and use in source and binary forms, with or without
12156952Sume * modification, are permitted provided that the following conditions
13156952Sume * are met:
14156952Sume * 1. Redistributions of source code must retain the above copyright
15156952Sume *    notice, this list of conditions and the following disclaimer.
16156952Sume * 2. Redistributions in binary form must reproduce the above copyright
17156952Sume *    notice, this list of conditions and the following disclaimer in the
18156952Sume *    documentation and/or other materials provided with the distribution.
19170242Sume * 3. The names of the authors may not be used to endorse or promote
20156952Sume *    products derived from this software without specific prior written
21156952Sume *    permission.
22156952Sume *
23156952Sume * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
24156952Sume * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
25156952Sume * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
26156952Sume * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
27156952Sume * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
28156952Sume * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
29156952Sume * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
30156952Sume * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
31156952Sume * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
32156952Sume * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
33156952Sume * SUCH DAMAGE.
34156952Sume *
35156952Sume * $FreeBSD: head/sys/geom/geom_ctl.c 112876 2003-03-31 18:35:37Z phk $
36156952Sume */
37156952Sume
38156952Sume#include "opt_geom.h"
39156952Sume
40156952Sume#include <sys/param.h>
41156952Sume#include <sys/systm.h>
42156952Sume#include <sys/kernel.h>
43156952Sume#include <sys/sysctl.h>
44170242Sume#include <sys/bio.h>
45156952Sume#include <sys/conf.h>
46156952Sume#include <sys/disk.h>
47156952Sume#include <sys/malloc.h>
48156952Sume#include <sys/sysctl.h>
49156952Sume
50156952Sume#include <sys/lock.h>
51156952Sume#include <sys/mutex.h>
52156952Sume
53156952Sume#include <vm/vm.h>
54156952Sume#include <vm/vm_extern.h>
55156952Sume
56156952Sume#include <geom/geom.h>
57156952Sume#include <geom/geom_int.h>
58156952Sume#define GCTL_TABLE 1
59156952Sume#include <geom/geom_ctl.h>
60156952Sume#include <geom/geom_ext.h>
61156952Sume
62156952Sumestatic d_ioctl_t g_ctl_ioctl;
63156952Sume
64156952Sumestatic struct cdevsw g_ctl_cdevsw = {
65156952Sume	.d_open =	nullopen,
66156952Sume	.d_close =	nullclose,
67156952Sume	.d_ioctl =	g_ctl_ioctl,
68156952Sume	.d_name =	"g_ctl",
69156952Sume};
70156952Sume
71156952Sumevoid
72156952Sumeg_ctl_init(void)
73156952Sume{
74156952Sume
75156952Sume	make_dev(&g_ctl_cdevsw, 0,
76156952Sume	    UID_ROOT, GID_OPERATOR, 0640, PATH_GEOM_CTL);
77156952Sume	KASSERT(GCTL_PARAM_RD == VM_PROT_READ,
78156952Sume		("GCTL_PARAM_RD != VM_PROT_READ"));
79156952Sume	KASSERT(GCTL_PARAM_WR == VM_PROT_WRITE,
80156952Sume		("GCTL_PARAM_WR != VM_PROT_WRITE"));
81156952Sume}
82156952Sume
83156952Sumestatic int
84156952Sumeg_ctl_ioctl_configgeom(dev_t dev, u_long cmd, caddr_t data, int fflag, struct thread *td)
85156952Sume{
86170242Sume	struct geomconfiggeom *gcp;
87156952Sume	struct g_configargs ga;
88170242Sume	int error;
89156952Sume
90170242Sume	error = 0;
91170242Sume	bzero(&ga, sizeof ga);
92156952Sume	gcp = (struct geomconfiggeom *)data;
93170242Sume	ga.class = g_idclass(&gcp->class);
94170242Sume	if (ga.class == NULL)
95156952Sume		return (EINVAL);
96156952Sume	if (ga.class->config == NULL)
97156952Sume		return (EOPNOTSUPP);
98156952Sume	ga.geom = g_idgeom(&gcp->geom);
99156952Sume	ga.provider = g_idprovider(&gcp->provider);
100156952Sume	ga.len = gcp->len;
101156952Sume	if (gcp->len > 64 * 1024)
102156952Sume		return (EINVAL);
103156952Sume	else if (gcp->len == 0) {
104156952Sume		ga.ptr = NULL;
105156952Sume	} else {
106156952Sume		ga.ptr = g_malloc(gcp->len, M_WAITOK);
107156952Sume		error = copyin(gcp->ptr, ga.ptr, gcp->len);
108156952Sume		if (error) {
109156952Sume			g_free(ga.ptr);
110156952Sume			return (error);
111156952Sume		}
112156952Sume	}
113156952Sume	ga.flag = gcp->flag;
114156952Sume	error = ga.class->config(&ga);
115156952Sume	if (gcp->len != 0)
116156952Sume		copyout(ga.ptr, gcp->ptr, gcp->len);	/* Ignore error */
117156952Sume	gcp->class.u.id = (uintptr_t)ga.class;
118156952Sume	gcp->class.len = 0;
119156952Sume	gcp->geom.u.id = (uintptr_t)ga.geom;
120156952Sume	gcp->geom.len = 0;
121156952Sume	gcp->provider.u.id = (uintptr_t)ga.provider;
122156952Sume	gcp->provider.len = 0;
123170242Sume	return(error);
124156952Sume}
125156952Sume
126156952Sume/*
127156952Sume * Report an error back to the user in ascii format.  Return whatever copyout
128156952Sume * returned, or EINVAL if it succeeded.
129156952Sume * XXX: should not be static.
130156952Sume * XXX: should take printf like args.
131156952Sume */
132156952Sumeint
133156952Sumegctl_error(struct gctl_req *req, const char *errtxt)
134156952Sume{
135156952Sume	int error;
136156952Sume
137156952Sume	error = copyout(errtxt, req->error,
138156952Sume	    imin(req->lerror, strlen(errtxt) + 1));
139156952Sume	if (!error)
140156952Sume		error = EINVAL;
141156952Sume	return (error);
142156952Sume}
143156952Sume
144156952Sume/*
145156952Sume * Allocate space and copyin() something.
146156952Sume * XXX: this should really be a standard function in the kernel.
147156952Sume */
148156952Sumestatic void *
149156952Sumegeom_alloc_copyin(struct gctl_req *req, void *uaddr, size_t len, int *errp)
150156952Sume{
151156952Sume	int error;
152156952Sume	void *ptr;
153156952Sume
154156952Sume	ptr = g_malloc(len, M_WAITOK);
155156952Sume	if (ptr == NULL)
156156952Sume		error = ENOMEM;
157156952Sume	else
158156952Sume		error = copyin(uaddr, ptr, len);
159156952Sume	if (!error)
160156952Sume		return (ptr);
161156952Sume	gctl_error(req, "no access to argument");
162156952Sume	*errp = error;
163156952Sume	if (ptr != NULL)
164156952Sume		g_free(ptr);
165156952Sume	return (NULL);
166156952Sume}
167156952Sume
168156952Sume
169156952Sume/*
170156952Sume * XXX: This function is a nightmare.  It walks through the request and
171156952Sume * XXX: makes sure that the various bits and pieces are there and copies
172156952Sume * XXX: some of them into kernel memory to make things easier.
173156952Sume * XXX: I really wish we had a standard marshalling layer somewhere.
174156952Sume */
175156952Sume
176156952Sumestatic int
177156952Sumegctl_copyin(struct gctl_req *req)
178156952Sume{
179156952Sume	int error, i, j;
180156952Sume	struct gctl_req_arg *ap;
181156952Sume	char *p;
182156952Sume
183156952Sume	error = 0;
184156952Sume	ap = geom_alloc_copyin(req, req->arg, req->narg * sizeof(*ap), &error);
185156952Sume	if (ap == NULL) {
186156952Sume		gctl_error(req, "copyin() of arguments failed");
187156952Sume		return (error);
188170242Sume	}
189156952Sume
190170242Sume	for (i = 0; !error && i < req->narg; i++) {
191156952Sume		if (ap[i].len > 0 &&
192170242Sume		    !useracc(ap[i].value, ap[i].len,
193170242Sume		    ap[i].flag & GCTL_PARAM_RW))
194170242Sume			error = gctl_error(req, "no access to param data");
195170242Sume		if (ap[i].name == NULL) {
196170242Sume			if (req->reqt->meta)
197156952Sume				continue;
198170242Sume			error = gctl_error(req,
199156952Sume			    "request does not take metadata arguments");
200156952Sume			break;
201156952Sume		}
202156952Sume		p = NULL;
203156952Sume		if (ap[i].nlen < 1 || ap[i].nlen > SPECNAMELEN) {
204156952Sume			error = gctl_error(req, "wrong param name length");
205156952Sume			break;
206156952Sume		}
207156952Sume		p = geom_alloc_copyin(req, ap[i].name, ap[i].nlen, &error);
208156952Sume		if (p == NULL)
209156952Sume			break;
210156952Sume		if (p[ap[i].nlen - 1] != '\0') {
211156952Sume			error = gctl_error(req, "unterminated param name");
212156952Sume			g_free(p);
213156952Sume			break;
214156952Sume		}
215170242Sume		ap[i].name = p;
216156952Sume		ap[i].nlen = 0;
217170242Sume	}
218156952Sume	if (!error) {
219156952Sume		req->arg = ap;
220156952Sume		return (0);
221156952Sume	}
222156952Sume	for (j = 0; j < i; j++)
223156952Sume		if (ap[j].nlen == 0 && ap[j].name != NULL)
224156952Sume			g_free(ap[j].name);
225156952Sume	g_free(ap);
226156952Sume	return (error);
227156952Sume}
228156952Sume
229156952Sumestatic void
230156952Sumegctl_dump(struct gctl_req *req)
231156952Sume{
232156952Sume	u_int i;
233156952Sume	int j, error;
234156952Sume	struct gctl_req_arg *ap;
235156952Sume	void *p;
236156952Sume
237156952Sume
238156952Sume	printf("Dump of gctl %s request at %p:\n", req->reqt->name, req);
239156952Sume	if (req->lerror > 0) {
240156952Sume		p = geom_alloc_copyin(req, req->error, req->lerror, &error);
241156952Sume		if (p != NULL) {
242156952Sume			((char *)p)[req->lerror - 1] = '\0';
243156952Sume			printf("  error:\t\"%s\"\n", (char *)p);
244156952Sume			g_free(p);
245156952Sume		}
246156952Sume	}
247156952Sume	for (i = 0; i < req->narg; i++) {
248156952Sume		ap = &req->arg[i];
249156952Sume		if (ap->name != NULL)
250156952Sume			printf("  param:\t\"%s\"", ap->name);
251156952Sume		else
252156952Sume			printf("  meta:\t@%jd", (intmax_t)ap->offset);
253156952Sume		printf(" [%s%s%d] = ",
254156952Sume		    ap->flag & GCTL_PARAM_RD ? "R" : "",
255156952Sume		    ap->flag & GCTL_PARAM_WR ? "W" : "",
256156952Sume		    ap->len);
257156952Sume		if (ap->flag & GCTL_PARAM_ASCII) {
258156952Sume			p = geom_alloc_copyin(req, ap->value, ap->len, &error);
259156952Sume			if (p != NULL) {
260156952Sume				((char *)p)[ap->len - 1] = '\0';
261156952Sume				printf("\"%s\"", (char *)p);
262170242Sume			}
263156952Sume			g_free(p);
264156952Sume		} else if (ap->len > 0) {
265156952Sume			p = geom_alloc_copyin(req, ap->value, ap->len, &error);
266156952Sume			for (j = 0; j < ap->len; j++)
267156952Sume				printf(" %02x", ((u_char *)p)[j]);
268156952Sume			g_free(p);
269156952Sume		} else {
270156952Sume			printf(" = %p", ap->value);
271156952Sume		}
272156952Sume		printf("\n");
273156952Sume	}
274156952Sume}
275156952Sume
276156952Sumevoid *
277156952Sumegctl_get_param(struct gctl_req *req, const char *param, int *len)
278156952Sume{
279156952Sume	int i, error, j;
280156952Sume	void *p;
281156952Sume	struct gctl_req_arg *ap;
282156952Sume
283156952Sume	for (i = 0; i < req->narg; i++) {
284156952Sume		ap = &req->arg[i];
285156952Sume		if (strcmp(param, ap->name))
286156952Sume			continue;
287156952Sume		if (!(ap->flag & GCTL_PARAM_RD))
288156952Sume			continue;
289156952Sume		if (ap->len > 0)
290156952Sume			j = ap->len;
291156952Sume		else
292156952Sume			j = 0;
293156952Sume		if (j != 0)
294156952Sume			p = geom_alloc_copyin(req, ap->value, j, &error);
295156952Sume			/* XXX: should not fail, tested prviously */
296156952Sume		else
297156952Sume			p = ap->value;
298156952Sume		if (len != NULL)
299156952Sume			*len = j;
300170242Sume		return (p);
301156952Sume	}
302156952Sume	return (NULL);
303156952Sume}
304156952Sume
305156952Sumestatic struct g_class*
306156952Sumegctl_get_class(struct gctl_req *req)
307156952Sume{
308156952Sume	char *p;
309156952Sume	int len;
310156952Sume	struct g_class *cp;
311156952Sume
312156952Sume	p = gctl_get_param(req, "class", &len);
313156952Sume	if (p == NULL)
314156952Sume		return (NULL);
315156952Sume	if (p[len - 1] != '\0') {
316156952Sume		gctl_error(req, "Unterminated class name");
317170242Sume		g_free(p);
318156952Sume		return (NULL);
319156952Sume	}
320156952Sume	LIST_FOREACH(cp, &g_classes, class) {
321156952Sume		if (!strcmp(p, cp->name)) {
322156952Sume			g_free(p);
323156952Sume			return (cp);
324170242Sume		}
325156952Sume	}
326170242Sume	gctl_error(req, "Class not found");
327156952Sume	return (NULL);
328170242Sume}
329170242Sume
330156952Sumestatic struct g_geom*
331170242Sumegctl_get_geom(struct gctl_req *req, struct g_class *mpr)
332156952Sume{
333156952Sume	char *p;
334156952Sume	int len;
335156952Sume	struct g_class *mp;
336156952Sume	struct g_geom *gp;
337156952Sume
338156952Sume	p = gctl_get_param(req, "geom", &len);
339156952Sume	if (p == NULL)
340156952Sume		return (NULL);
341156952Sume	if (p[len - 1] != '\0') {
342156952Sume		gctl_error(req, "Unterminated provider name");
343156952Sume		g_free(p);
344156952Sume		return (NULL);
345156952Sume	}
346156952Sume	LIST_FOREACH(mp, &g_classes, class) {
347156952Sume		if (mpr != NULL && mpr != mp)
348156952Sume			continue;
349156952Sume		LIST_FOREACH(gp, &mp->geom, geom) {
350156952Sume			if (!strcmp(p, gp->name)) {
351156952Sume				g_free(p);
352156952Sume				return (gp);
353156952Sume			}
354156952Sume		}
355156952Sume	}
356156952Sume	gctl_error(req, "Geom not found");
357156952Sume	return (NULL);
358156952Sume}
359156952Sume
360156952Sumestatic struct g_provider*
361156952Sumegctl_get_provider(struct gctl_req *req)
362156952Sume{
363156952Sume	char *p;
364156952Sume	int len;
365156952Sume	struct g_class *cp;
366156952Sume	struct g_geom *gp;
367156952Sume	struct g_provider *pp;
368156952Sume
369156952Sume	p = gctl_get_param(req, "provider", &len);
370156952Sume	if (p == NULL)
371156952Sume		return (NULL);
372156952Sume	if (p[len - 1] != '\0') {
373156952Sume		gctl_error(req, "Unterminated provider name");
374156952Sume		g_free(p);
375156952Sume		return (NULL);
376156952Sume	}
377156952Sume	LIST_FOREACH(cp, &g_classes, class) {
378170242Sume		LIST_FOREACH(gp, &cp->geom, geom) {
379156952Sume			LIST_FOREACH(pp, &gp->provider, provider) {
380170242Sume				if (!strcmp(p, pp->name)) {
381156952Sume					g_free(p);
382170242Sume					return (pp);
383156952Sume				}
384156952Sume			}
385156952Sume		}
386156952Sume	}
387156952Sume	gctl_error(req, "Provider not found");
388156952Sume	return (NULL);
389156952Sume}
390156952Sume
391156952Sumestatic void
392156952Sumegctl_create_geom(struct gctl_req *req)
393156952Sume{
394156952Sume	struct g_class *mp;
395156952Sume	struct g_provider *pp;
396156952Sume
397156952Sume	g_topology_assert();
398156952Sume	mp = gctl_get_class(req);
399156952Sume	if (mp == NULL)
400156952Sume		return;
401156952Sume	if (mp->create_geom == NULL) {
402156952Sume		gctl_error(req, "Class has no create_geom method");
403156952Sume		return;
404156952Sume	}
405156952Sume	pp = gctl_get_provider(req);
406156952Sume	mp->create_geom(req, mp, pp);
407156952Sume	g_topology_assert();
408156952Sume}
409156952Sume
410156952Sumestatic void
411156952Sumegctl_destroy_geom(struct gctl_req *req)
412156952Sume{
413156952Sume	struct g_class *mp;
414156952Sume	struct g_geom *gp;
415156952Sume
416156952Sume	g_topology_assert();
417156952Sume	mp = gctl_get_class(req);
418156952Sume	if (mp == NULL)
419156952Sume		return;
420156952Sume	if (mp->destroy_geom == NULL) {
421156952Sume		gctl_error(req, "Class has no destroy_geom method");
422156952Sume		return;
423156952Sume	}
424156952Sume	gp = gctl_get_geom(req, mp);
425156952Sume	if (gp == NULL) {
426156952Sume		gctl_error(req, "Geom not specified");
427156952Sume		return;
428156952Sume	}
429156952Sume	if (gp->class != mp) {
430156952Sume		gctl_error(req, "Geom not of specificed class");
431170242Sume		return;
432156952Sume	}
433156952Sume	mp->destroy_geom(req, mp, gp);
434156952Sume	g_topology_assert();
435156952Sume}
436156952Sume
437156952Sume/*
438156952Sume * Handle ioctl from libgeom::geom_ctl.c
439156952Sume */
440156952Sumestatic int
441156952Sumeg_ctl_ioctl_ctl(dev_t dev, u_long cmd, caddr_t data, int fflag, struct thread *td)
442156952Sume{
443156952Sume	int error;
444156952Sume	int i;
445156952Sume	struct gctl_req *req;
446156952Sume
447156952Sume	req = (void *)data;
448156952Sume	/* It is an error if we cannot return an error text */
449170242Sume	if (req->lerror < 1)
450156952Sume		return (EINVAL);
451156952Sume	if (!useracc(req->error, req->lerror, VM_PROT_WRITE))
452156952Sume		return (EINVAL);
453156952Sume
454156952Sume	/* Check the version */
455156952Sume	if (req->version != GCTL_VERSION)
456156952Sume		return (gctl_error(req,
457156952Sume		    "kernel and libgeom version mismatch."));
458170242Sume
459156952Sume	/* Check the request type */
460170242Sume	for (i = 0; gcrt[i].request != GCTL_INVALID_REQUEST; i++)
461156952Sume		if (gcrt[i].request == req->request)
462170242Sume			break;
463170242Sume	if (gcrt[i].request == GCTL_INVALID_REQUEST)
464156952Sume		return (gctl_error(req, "invalid request"));
465170242Sume	req->reqt = &gcrt[i];
466170242Sume
467156952Sume	/* Get things on board */
468170242Sume	error = gctl_copyin(req);
469156952Sume	if (error)
470170242Sume		return (error);
471156952Sume
472170242Sume	if (g_debugflags & G_F_CTLDUMP)
473156952Sume		gctl_dump(req);
474156952Sume#if 0
475156952Sume	g_stall_events();
476156952Sume#endif
477156952Sume	g_topology_lock();
478156952Sume	switch (req->request) {
479156952Sume	case GCTL_CREATE_GEOM:
480156952Sume		gctl_create_geom(req);
481156952Sume		break;
482156952Sume	case GCTL_DESTROY_GEOM:
483156952Sume		gctl_destroy_geom(req);
484156952Sume		break;
485156952Sume	default:
486156952Sume		gctl_error(req, "XXX: TBD");
487156952Sume		break;
488156952Sume	}
489156952Sume	g_topology_unlock();
490156952Sume#if 0
491156952Sume	g_release_events();
492156952Sume#endif
493156952Sume	return (0);
494170242Sume}
495156952Sume
496156952Sumestatic int
497156952Sumeg_ctl_ioctl(dev_t dev, u_long cmd, caddr_t data, int fflag, struct thread *td)
498156952Sume{
499156952Sume	int error;
500156952Sume
501156952Sume	switch(cmd) {
502156952Sume	case GEOMCONFIGGEOM:
503156952Sume		DROP_GIANT();
504156952Sume		g_topology_lock();
505156952Sume		error = g_ctl_ioctl_configgeom(dev, cmd, data, fflag, td);
506156952Sume		g_topology_unlock();
507156952Sume		PICKUP_GIANT();
508156952Sume		break;
509156952Sume	case GEOM_CTL:
510156952Sume		DROP_GIANT();
511156952Sume		error = g_ctl_ioctl_ctl(dev, cmd, data, fflag, td);
512156952Sume		PICKUP_GIANT();
513156952Sume		break;
514156952Sume	default:
515156952Sume		error = ENOTTY;
516156952Sume		break;
517156952Sume	}
518156952Sume	return (error);
519156952Sume
520156952Sume}
521156952Sume