xref: /freebsd/sys/geom/geom_ctl.c (revision 425e57e7a2f754f7be8425bf3b00ce1469aba5b0)
1 /*-
2  * SPDX-License-Identifier: BSD-3-Clause
3  *
4  * Copyright (c) 2002 Poul-Henning Kamp
5  * Copyright (c) 2002 Networks Associates Technology, Inc.
6  * All rights reserved.
7  *
8  * This software was developed for the FreeBSD Project by Poul-Henning Kamp
9  * and NAI Labs, the Security Research Division of Network Associates, Inc.
10  * under DARPA/SPAWAR contract N66001-01-C-8035 ("CBOSS"), as part of the
11  * DARPA CHATS research program.
12  *
13  * Redistribution and use in source and binary forms, with or without
14  * modification, are permitted provided that the following conditions
15  * are met:
16  * 1. Redistributions of source code must retain the above copyright
17  *    notice, this list of conditions and the following disclaimer.
18  * 2. Redistributions in binary form must reproduce the above copyright
19  *    notice, this list of conditions and the following disclaimer in the
20  *    documentation and/or other materials provided with the distribution.
21  * 3. The names of the authors may not be used to endorse or promote
22  *    products derived from this software without specific prior written
23  *    permission.
24  *
25  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
26  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
27  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
28  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
29  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
30  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
31  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
32  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
33  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
34  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
35  * SUCH DAMAGE.
36  */
37 
38 #include <sys/cdefs.h>
39 __FBSDID("$FreeBSD$");
40 
41 #include <sys/param.h>
42 #include <sys/systm.h>
43 #include <sys/conf.h>
44 #include <sys/malloc.h>
45 #include <sys/sbuf.h>
46 
47 #include <vm/vm.h>
48 #include <vm/vm_extern.h>
49 
50 #include <geom/geom.h>
51 #include <geom/geom_int.h>
52 #define GCTL_TABLE 1
53 #include <geom/geom_ctl.h>
54 
55 #include <machine/stdarg.h>
56 
57 static d_ioctl_t g_ctl_ioctl;
58 
59 static struct cdevsw g_ctl_cdevsw = {
60 	.d_version =	D_VERSION,
61 	.d_flags =	0,
62 	.d_ioctl =	g_ctl_ioctl,
63 	.d_name =	"g_ctl",
64 };
65 
66 CTASSERT(GCTL_PARAM_RD == VM_PROT_READ);
67 CTASSERT(GCTL_PARAM_WR == VM_PROT_WRITE);
68 
69 void
70 g_ctl_init(void)
71 {
72 
73 	make_dev_credf(MAKEDEV_ETERNAL, &g_ctl_cdevsw, 0, NULL,
74 	    UID_ROOT, GID_OPERATOR, 0640, PATH_GEOM_CTL);
75 }
76 
77 /*
78  * Report an error back to the user in ascii format.  Return nerror
79  * or EINVAL if nerror isn't specified.
80  */
81 int
82 gctl_error(struct gctl_req *req, const char *fmt, ...)
83 {
84 	va_list ap;
85 
86 	if (req == NULL)
87 		return (EINVAL);
88 
89 	/* We only record the first error */
90 	if (sbuf_done(req->serror)) {
91 		if (!req->nerror)
92 			req->nerror = EEXIST;
93 #ifdef DIAGNOSTIC
94 		printf("gctl_error: buffer closed, message discarded.\n");
95 #endif
96 		return (req->nerror);
97 	}
98 	if (!req->nerror)
99 		req->nerror = EINVAL;
100 
101 	/* If this is the last of several messages, indent it on a new line */
102 	if (sbuf_len(req->serror) > 0)
103 		sbuf_cat(req->serror, "\n\t");
104 	va_start(ap, fmt);
105 	sbuf_vprintf(req->serror, fmt, ap);
106 	va_end(ap);
107 	gctl_post_messages(req);
108 	return (req->nerror);
109 }
110 
111 /*
112  * The gctl_error() function will only report a single message.
113  * Commands that handle multiple devices may want to report a
114  * message for each of the devices. The gctl_msg() function
115  * can be called multiple times to post messages. When done
116  * the application must either call gctl_post_messages() or
117  * call gctl_error() to cause the messages to be reported to
118  * the calling process.
119  */
120 void
121 gctl_msg(struct gctl_req *req, const char *fmt, ...)
122 {
123 	va_list ap;
124 
125 	if (req == NULL)
126 		return;
127 	if (sbuf_done(req->serror)) {
128 #ifdef DIAGNOSTIC
129 		printf("gctl_msg: buffer closed, message discarded.\n");
130 #endif
131 		return;
132 	}
133 	/* Put second and later messages indented on a new line */
134 	if (sbuf_len(req->serror) > 0)
135 		sbuf_cat(req->serror, "\n\t");
136 	va_start(ap, fmt);
137 	sbuf_vprintf(req->serror, fmt, ap);
138 	va_end(ap);
139 }
140 
141 /*
142  * Post the messages to the user.
143  */
144 void
145 gctl_post_messages(struct gctl_req *req)
146 {
147 
148 	if (sbuf_done(req->serror)) {
149 #ifdef DIAGNOSTIC
150 		printf("gctl_post_messages: message buffer already closed.\n");
151 #endif
152 		return;
153 	}
154 	sbuf_finish(req->serror);
155 	if (g_debugflags & G_F_CTLDUMP)
156 		printf("gctl %p message(s) \"%s\"\n", req,
157 		    sbuf_data(req->serror));
158 }
159 
160 /*
161  * Allocate space and copyin() something.
162  * XXX: this should really be a standard function in the kernel.
163  */
164 static void *
165 geom_alloc_copyin(struct gctl_req *req, void *uaddr, size_t len)
166 {
167 	void *ptr;
168 
169 	ptr = g_malloc(len, M_WAITOK);
170 	req->nerror = copyin(uaddr, ptr, len);
171 	if (!req->nerror)
172 		return (ptr);
173 	g_free(ptr);
174 	return (NULL);
175 }
176 
177 static void
178 gctl_copyin(struct gctl_req *req)
179 {
180 	struct gctl_req_arg *ap;
181 	char *p;
182 	u_int i;
183 
184 	if (req->narg > GEOM_CTL_ARG_MAX) {
185 		gctl_error(req, "too many arguments");
186 		req->arg = NULL;
187 		return;
188 	}
189 
190 	ap = geom_alloc_copyin(req, req->arg, req->narg * sizeof(*ap));
191 	if (ap == NULL) {
192 		gctl_error(req, "bad control request");
193 		req->arg = NULL;
194 		return;
195 	}
196 
197 	/* Nothing have been copyin()'ed yet */
198 	for (i = 0; i < req->narg; i++) {
199 		ap[i].flag &= ~(GCTL_PARAM_NAMEKERNEL|GCTL_PARAM_VALUEKERNEL);
200 		ap[i].flag &= ~GCTL_PARAM_CHANGED;
201 		ap[i].kvalue = NULL;
202 	}
203 
204 	for (i = 0; i < req->narg; i++) {
205 		if (ap[i].nlen < 1 || ap[i].nlen > SPECNAMELEN) {
206 			gctl_error(req,
207 			    "wrong param name length %d: %d", i, ap[i].nlen);
208 			break;
209 		}
210 		p = geom_alloc_copyin(req, ap[i].name, ap[i].nlen);
211 		if (p == NULL)
212 			break;
213 		if (p[ap[i].nlen - 1] != '\0') {
214 			gctl_error(req, "unterminated param name");
215 			g_free(p);
216 			break;
217 		}
218 		ap[i].name = p;
219 		ap[i].flag |= GCTL_PARAM_NAMEKERNEL;
220 		if (ap[i].len <= 0) {
221 			gctl_error(req, "negative param length");
222 			break;
223 		}
224 		if (ap[i].flag & GCTL_PARAM_RD) {
225 			p = geom_alloc_copyin(req, ap[i].value, ap[i].len);
226 			if (p == NULL)
227 				break;
228 			if ((ap[i].flag & GCTL_PARAM_ASCII) &&
229 			    p[ap[i].len - 1] != '\0') {
230 				gctl_error(req, "unterminated param value");
231 				g_free(p);
232 				break;
233 			}
234 		} else {
235 			p = g_malloc(ap[i].len, M_WAITOK | M_ZERO);
236 		}
237 		ap[i].kvalue = p;
238 		ap[i].flag |= GCTL_PARAM_VALUEKERNEL;
239 	}
240 	req->arg = ap;
241 	return;
242 }
243 
244 static void
245 gctl_copyout(struct gctl_req *req)
246 {
247 	int error, i;
248 	struct gctl_req_arg *ap;
249 
250 	if (req->nerror)
251 		return;
252 	error = 0;
253 	ap = req->arg;
254 	for (i = 0; i < req->narg; i++, ap++) {
255 		if (!(ap->flag & GCTL_PARAM_CHANGED))
256 			continue;
257 		error = copyout(ap->kvalue, ap->value, ap->len);
258 		if (!error)
259 			continue;
260 		req->nerror = error;
261 		return;
262 	}
263 	return;
264 }
265 
266 static void
267 gctl_free(struct gctl_req *req)
268 {
269 	u_int i;
270 
271 	sbuf_delete(req->serror);
272 	if (req->arg == NULL)
273 		return;
274 	for (i = 0; i < req->narg; i++) {
275 		if (req->arg[i].flag & GCTL_PARAM_NAMEKERNEL)
276 			g_free(req->arg[i].name);
277 		if ((req->arg[i].flag & GCTL_PARAM_VALUEKERNEL) &&
278 		    req->arg[i].len > 0)
279 			g_free(req->arg[i].kvalue);
280 	}
281 	g_free(req->arg);
282 }
283 
284 static void
285 gctl_dump(struct gctl_req *req, const char *what)
286 {
287 	struct gctl_req_arg *ap;
288 	u_int i;
289 	int j;
290 
291 	printf("Dump of gctl %s at %p:\n", what, req);
292 	if (req->nerror > 0) {
293 		printf("  nerror:\t%d\n", req->nerror);
294 		if (sbuf_len(req->serror) > 0)
295 			printf("  error:\t\"%s\"\n", sbuf_data(req->serror));
296 	}
297 	if (req->arg == NULL)
298 		return;
299 	for (i = 0; i < req->narg; i++) {
300 		ap = &req->arg[i];
301 		if (!(ap->flag & GCTL_PARAM_NAMEKERNEL))
302 			printf("  param:\t%d@%p", ap->nlen, ap->name);
303 		else
304 			printf("  param:\t\"%s\"", ap->name);
305 		printf(" [%s%s%d] = ",
306 		    ap->flag & GCTL_PARAM_RD ? "R" : "",
307 		    ap->flag & GCTL_PARAM_WR ? "W" : "",
308 		    ap->len);
309 		if (!(ap->flag & GCTL_PARAM_VALUEKERNEL)) {
310 			printf(" =@ %p", ap->value);
311 		} else if (ap->flag & GCTL_PARAM_ASCII) {
312 			printf("\"%s\"", (char *)ap->kvalue);
313 		} else if (ap->len > 0) {
314 			for (j = 0; j < ap->len && j < 512; j++)
315 				printf(" %02x", ((u_char *)ap->kvalue)[j]);
316 		} else {
317 			printf(" = %p", ap->kvalue);
318 		}
319 		printf("\n");
320 	}
321 }
322 
323 int
324 gctl_set_param(struct gctl_req *req, const char *param, void const *ptr,
325     int len)
326 {
327 	u_int i;
328 	struct gctl_req_arg *ap;
329 
330 	for (i = 0; i < req->narg; i++) {
331 		ap = &req->arg[i];
332 		if (strcmp(param, ap->name))
333 			continue;
334 		if (!(ap->flag & GCTL_PARAM_WR))
335 			return (EPERM);
336 		ap->flag |= GCTL_PARAM_CHANGED;
337 		if (ap->len < len) {
338 			bcopy(ptr, ap->kvalue, ap->len);
339 			return (ENOSPC);
340 		}
341 		bcopy(ptr, ap->kvalue, len);
342 		return (0);
343 	}
344 	return (EINVAL);
345 }
346 
347 void
348 gctl_set_param_err(struct gctl_req *req, const char *param, void const *ptr,
349     int len)
350 {
351 
352 	switch (gctl_set_param(req, param, ptr, len)) {
353 	case EPERM:
354 		gctl_error(req, "No write access %s argument", param);
355 		break;
356 	case ENOSPC:
357 		gctl_error(req, "Wrong length %s argument", param);
358 		break;
359 	case EINVAL:
360 		gctl_error(req, "Missing %s argument", param);
361 		break;
362 	}
363 }
364 
365 void *
366 gctl_get_param(struct gctl_req *req, const char *param, int *len)
367 {
368 	u_int i;
369 	void *p;
370 	struct gctl_req_arg *ap;
371 
372 	for (i = 0; i < req->narg; i++) {
373 		ap = &req->arg[i];
374 		if (strcmp(param, ap->name))
375 			continue;
376 		if (!(ap->flag & GCTL_PARAM_RD))
377 			continue;
378 		p = ap->kvalue;
379 		if (len != NULL)
380 			*len = ap->len;
381 		return (p);
382 	}
383 	return (NULL);
384 }
385 
386 char const *
387 gctl_get_asciiparam(struct gctl_req *req, const char *param)
388 {
389 	u_int i;
390 	char const *p;
391 	struct gctl_req_arg *ap;
392 
393 	for (i = 0; i < req->narg; i++) {
394 		ap = &req->arg[i];
395 		if (strcmp(param, ap->name))
396 			continue;
397 		if (!(ap->flag & GCTL_PARAM_RD))
398 			continue;
399 		p = ap->kvalue;
400 		if (ap->len < 1) {
401 			gctl_error(req, "No length argument (%s)", param);
402 			return (NULL);
403 		}
404 		if (p[ap->len - 1] != '\0') {
405 			gctl_error(req, "Unterminated argument (%s)", param);
406 			return (NULL);
407 		}
408 		return (p);
409 	}
410 	return (NULL);
411 }
412 
413 void *
414 gctl_get_paraml_opt(struct gctl_req *req, const char *param, int len)
415 {
416 	int i;
417 	void *p;
418 
419 	p = gctl_get_param(req, param, &i);
420 	if (i != len) {
421 		p = NULL;
422 		gctl_error(req, "Wrong length %s argument", param);
423 	}
424 	return (p);
425 }
426 
427 void *
428 gctl_get_paraml(struct gctl_req *req, const char *param, int len)
429 {
430 	void *p;
431 
432 	p = gctl_get_paraml_opt(req, param, len);
433 	if (p == NULL)
434 		gctl_error(req, "Missing %s argument", param);
435 	return (p);
436 }
437 
438 struct g_class *
439 gctl_get_class(struct gctl_req *req, char const *arg)
440 {
441 	char const *p;
442 	struct g_class *cp;
443 
444 	p = gctl_get_asciiparam(req, arg);
445 	if (p == NULL) {
446 		gctl_error(req, "Missing %s argument", arg);
447 		return (NULL);
448 	}
449 	LIST_FOREACH(cp, &g_classes, class) {
450 		if (!strcmp(p, cp->name))
451 			return (cp);
452 	}
453 	gctl_error(req, "Class not found: \"%s\"", p);
454 	return (NULL);
455 }
456 
457 struct g_geom *
458 gctl_get_geom(struct gctl_req *req, struct g_class *mp, char const *arg)
459 {
460 	char const *p;
461 	struct g_geom *gp;
462 
463 	MPASS(mp != NULL);
464 	p = gctl_get_asciiparam(req, arg);
465 	if (p == NULL) {
466 		gctl_error(req, "Missing %s argument", arg);
467 		return (NULL);
468 	}
469 	LIST_FOREACH(gp, &mp->geom, geom)
470 		if (!strcmp(p, gp->name))
471 			return (gp);
472 	gctl_error(req, "Geom not found: \"%s\"", p);
473 	return (NULL);
474 }
475 
476 struct g_provider *
477 gctl_get_provider(struct gctl_req *req, char const *arg)
478 {
479 	char const *p;
480 	struct g_provider *pp;
481 
482 	p = gctl_get_asciiparam(req, arg);
483 	if (p == NULL) {
484 		gctl_error(req, "Missing '%s' argument", arg);
485 		return (NULL);
486 	}
487 	pp = g_provider_by_name(p);
488 	if (pp != NULL)
489 		return (pp);
490 	gctl_error(req, "Provider not found: \"%s\"", p);
491 	return (NULL);
492 }
493 
494 static void
495 g_ctl_req(void *arg, int flag __unused)
496 {
497 	struct g_class *mp;
498 	struct gctl_req *req;
499 	char const *verb;
500 
501 	g_topology_assert();
502 	req = arg;
503 	mp = gctl_get_class(req, "class");
504 	if (mp == NULL)
505 		return;
506 	if (mp->ctlreq == NULL) {
507 		gctl_error(req, "Class takes no requests");
508 		return;
509 	}
510 	verb = gctl_get_param(req, "verb", NULL);
511 	if (verb == NULL) {
512 		gctl_error(req, "Verb missing");
513 		return;
514 	}
515 	mp->ctlreq(req, mp, verb);
516 	g_topology_assert();
517 }
518 
519 static int
520 g_ctl_ioctl_ctl(struct cdev *dev, u_long cmd, caddr_t data, int fflag, struct thread *td)
521 {
522 	struct gctl_req *req;
523 	int nerror;
524 
525 	req = (void *)data;
526 	req->nerror = 0;
527 	/* It is an error if we cannot return an error text */
528 	if (req->lerror < 2)
529 		return (EINVAL);
530 	if (!useracc(req->error, req->lerror, VM_PROT_WRITE))
531 		return (EINVAL);
532 
533 	req->serror = sbuf_new_auto();
534 	/* Check the version */
535 	if (req->version != GCTL_VERSION) {
536 		gctl_error(req, "kernel and libgeom version mismatch.");
537 		req->arg = NULL;
538 	} else {
539 		/* Get things on board */
540 		gctl_copyin(req);
541 
542 		if (g_debugflags & G_F_CTLDUMP)
543 			gctl_dump(req, "request");
544 
545 		if (!req->nerror) {
546 			g_waitfor_event(g_ctl_req, req, M_WAITOK, NULL);
547 
548 			if (g_debugflags & G_F_CTLDUMP)
549 				gctl_dump(req, "result");
550 
551 			gctl_copyout(req);
552 		}
553 	}
554 	if (sbuf_done(req->serror)) {
555 		copyout(sbuf_data(req->serror), req->error,
556 		    imin(req->lerror, sbuf_len(req->serror) + 1));
557 	}
558 
559 	nerror = req->nerror;
560 	gctl_free(req);
561 	return (nerror);
562 }
563 
564 static int
565 g_ctl_ioctl(struct cdev *dev, u_long cmd, caddr_t data, int fflag, struct thread *td)
566 {
567 	int error;
568 
569 	switch(cmd) {
570 	case GEOM_CTL:
571 		error = g_ctl_ioctl_ctl(dev, cmd, data, fflag, td);
572 		break;
573 	default:
574 		error = ENOIOCTL;
575 		break;
576 	}
577 	return (error);
578 
579 }
580