xref: /freebsd/sys/geom/geom_ctl.c (revision 729362425c09cf6b362366aabc6fb547eee8035a)
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$
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 
62 static d_ioctl_t g_ctl_ioctl;
63 
64 static 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 
71 void
72 g_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 
83 static int
84 g_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  */
132 int
133 gctl_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  */
148 static void *
149 geom_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 
176 static int
177 gctl_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 
229 static void
230 gctl_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 
276 void *
277 gctl_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 
305 static struct g_class*
306 gctl_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 
330 static struct g_geom*
331 gctl_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 
360 static struct g_provider*
361 gctl_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 
391 static void
392 gctl_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 
412 static void
413 gctl_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  */
444 static int
445 g_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 
499 static int
500 g_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