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