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