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