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